home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 1 / Atari Mega Archive - Volume 1.iso / tex / makeinfo.zoo / src / makeinfo.c < prev   
C/C++ Source or Header  |  1991-10-25  |  146KB  |  5,912 lines

  1. /* Makeinfo -- convert texinfo format files into info files
  2.    Copyright (C) 1987 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU Info.
  5.  
  6. GNU Info is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU Info is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Info; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* **************************************************************** */
  21. /*                                    */
  22. /*            Include File Declarations               */
  23. /*                                    */
  24. /* **************************************************************** */
  25.  
  26. #include <stdio.h>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <ctype.h>
  30. #include <pwd.h>
  31. #include <errno.h>
  32. #include <memory.h>
  33. #include <stdlib.h>
  34. #if __STDC__
  35. #include <stdarg.h>
  36. #endif
  37.  
  38. #include "getopt.h"
  39.  
  40. #if defined (VMS)
  41. #include <perror.h>
  42. #endif
  43.  
  44. #if defined (SYSV) || defined (VMS)
  45. #include <string.h>
  46. #else
  47. #include <strings.h>
  48. #endif
  49.  
  50. #include <fcntl.h>
  51. #include <sys/file.h>
  52.  
  53. #if defined (SYSV)
  54. #define bcopy(source, dest, count) memcpy (dest, source, count)
  55. #endif
  56.  
  57. #if defined (__GNUC__)
  58. #ifndef alloca
  59. #define alloca __builtin_alloca
  60. #endif
  61. #else
  62. #if defined (sparc)
  63. #include <alloca.h>
  64. #else
  65. extern char *alloca ();
  66. #endif
  67. #endif
  68.  
  69. #ifdef atarist
  70. #include <unistd.h>
  71. #include <support.h>
  72. #define DOTS
  73. #endif
  74. #define EOL "\n"
  75.  
  76. /* Forward declarations. */
  77. /* char *xmalloc (), *xrealloc (); */
  78.  
  79. #ifndef __PROTO
  80. #if defined(__STDC__) || defined(__cplusplus)
  81. # define __PROTO(s) s
  82. #else
  83. # define __PROTO(s) ()
  84. #endif
  85. #endif
  86.  
  87. /* Boolean values. */
  88. #define true  1
  89. #define false 0
  90. typedef int boolean;
  91.  
  92. /* The various references that we know about. */
  93. enum reftype
  94. {
  95.   menu_reference, followed_reference
  96. };
  97.  
  98. enum insertion_type
  99. {
  100.   menu, quotation, lisp, example, smallexample, display,
  101.   itemize, format, enumerate, table, group, ifinfo,
  102.   defun, defvar, defopt, deffn, defspec, defmac,
  103.   bad_type
  104. };
  105.  
  106. typedef int FUNCTION __PROTO((void));    /* So I can say FUNCTION *foo; */
  107. #define VFUNCTION void
  108.  
  109. typedef struct generic_list
  110. {
  111.   struct generic_list *next;
  112. }            GENERIC_LIST;
  113.  
  114. typedef struct
  115. {
  116.   char *name;
  117.   VFUNCTION *proc;
  118.   boolean argument_in_braces;
  119. } COMMAND;
  120.  
  121. /* What we remember for each node. */
  122. typedef struct tentry
  123. {
  124.   struct tentry *next_ent;
  125.   char *node;        /* name of this node. */
  126.   char *prev;        /* name of "Prev:" for this node. */
  127.   char *next;        /* name of "Next:" for this node. */
  128.   char *up;        /* name of "Up:" for this node.   */
  129.   size_t position;    /* output file position of this node. */
  130.   size_t line_no;    /* defining line in source file. */
  131.   char *filename;    /* The file that this node was found in. */
  132.   int touched;        /* non-zero means this node has been referenced. */
  133.   int flags;        /* Room for growth.  Right now, contains 1 bit. */
  134. } TAG_ENTRY;
  135.  
  136.  
  137. /* A structure to remember references with.  A reference to a node is
  138.    either an entry in a menu, or a cross-reference made with [px]ref. */
  139. typedef struct node_ref
  140. {
  141.   struct node_ref *next;
  142.   char *node;            /* Name of node referred to. */
  143.   char *containing_node;    /* Name of node containing this reference. */
  144.   size_t line_no;        /* Line number where the reference occurs. */
  145.   int section;            /* Section level where the reference occurs. */
  146.   char *filename;        /* Name of file where the reference occurs. */
  147.   enum reftype type;        /* Type of reference, either menu or note. */
  148. } NODE_REF;
  149.  
  150. /* An index element... */
  151. typedef struct index_elt
  152. {
  153.   struct index_elt *next;
  154.   char *entry;            /* The index entry itself. */
  155.   char *node;            /* The node from whence it came. */
  156.   int code;            /* Non-zero means add `@code{...}' when
  157.                    printing this element. */
  158. } INDEX_ELT;
  159.  
  160. /* A list of short-names for each index, and the index to that index in our
  161.    index array, the_indices.  In addition, for each index, it is remembered
  162.    whether that index is a code index or not.  Code indices have @code{}
  163.    inserted around the first word when they are printed with printindex. */
  164. typedef struct
  165. {
  166.   char *name;
  167.   int index;
  168.   int code;
  169. } INDEX_ALIST;
  170.  
  171. extern int main __PROTO((int argc, char **argv));
  172. extern void *xmalloc __PROTO((size_t nbytes));
  173. extern void *xrealloc __PROTO((void *pointer, size_t nbytes));
  174. extern void usage __PROTO((void));
  175. extern GENERIC_LIST *reverse_list __PROTO((GENERIC_LIST *list));
  176. extern char *find_and_load __PROTO((char *filename));
  177. extern void pushfile __PROTO((void));
  178. extern void popfile __PROTO((void));
  179. extern void flush_file_stack __PROTO((void));
  180. extern void push_node_filename __PROTO((void));
  181. extern void pop_node_filename __PROTO((void));
  182. extern char *filename_part __PROTO((char *filename));
  183. extern char *pathname_part __PROTO((char *filename));
  184. extern char *expand_filename __PROTO((char *filename, char *input_name));
  185. extern char *full_pathname __PROTO((char *filename));
  186. extern int fs_error __PROTO((char *filename));
  187. extern int error __PROTO((const char *format, ...));
  188. extern int line_error __PROTO((const char *format, ...));
  189. extern int warning __PROTO((const char *format, ...));
  190. extern void remember_error __PROTO((void));
  191. extern char *read_token __PROTO((void));
  192. extern boolean self_delimiting __PROTO((int character));
  193. extern void canon_white __PROTO((char *string));
  194. extern void fix_whitespace __PROTO((char *string));
  195. extern void discard_until __PROTO((char *string));
  196. extern void get_until __PROTO((char *match, char **string));
  197. extern void get_until_in_line __PROTO((char *match, char **string));
  198. extern void get_rest_of_line __PROTO((char **string));
  199. extern void get_until_in_braces __PROTO((char *match, char **string));
  200. extern void convert __PROTO((char *name));
  201. extern void free_and_clear __PROTO((char **pointer));
  202. extern void init_internals __PROTO((void));
  203. extern void init_paragraph __PROTO((void));
  204. extern void reader_loop __PROTO((void));
  205. extern COMMAND *get_command_entry __PROTO((char *string));
  206. extern void read_command __PROTO((void));
  207. extern char *find_proc_name __PROTO((FUNCTION *proc));
  208. extern void init_brace_stack __PROTO((void));
  209. extern void remember_brace __PROTO((VFUNCTION *proc));
  210. extern void remember_brace_1 __PROTO((FUNCTION *proc, size_t position));
  211. extern int pop_and_call_brace __PROTO((void));
  212. extern void discard_braces __PROTO((void));
  213. extern int get_char_len __PROTO((int character));
  214. extern void add_word_args __PROTO((const char *format, ...));
  215. extern void add_word __PROTO((char *string));
  216. extern void add_char __PROTO((int character));
  217. extern void insert __PROTO((int character));
  218. extern void kill_self_indent __PROTO((int count));
  219. extern void flush_output __PROTO((void));
  220. extern void close_single_paragraph __PROTO((void));
  221. extern void close_paragraph_with_lines __PROTO((int lines));
  222. extern void close_paragraph __PROTO((void));
  223. extern void start_paragraph __PROTO((void));
  224. extern void indent __PROTO((int amount));
  225. extern size_t search_forward __PROTO((char *string, size_t from));
  226. extern int stricmp __PROTO((char *string1, char *string2));
  227. extern int strnicmp __PROTO((char *string1, char *string2, size_t count));
  228. extern void init_insertion_stack __PROTO((void));
  229. extern enum insertion_type current_insertion_type __PROTO((void));
  230. extern char *current_item_function __PROTO((void));
  231. extern char *get_item_function __PROTO((void));
  232. extern void push_insertion __PROTO((enum insertion_type type,
  233.                      char *item_function));
  234. extern void pop_insertion __PROTO((void));
  235. extern char *insertion_type_pname __PROTO((enum insertion_type type));
  236. extern enum insertion_type find_type_from_name __PROTO((char *name));
  237. extern void do_nothing __PROTO((void));
  238. extern int defun_insertion __PROTO((enum insertion_type type));
  239. extern void begin_insertion __PROTO((enum insertion_type type));
  240. extern void end_insertion __PROTO((enum insertion_type type));
  241. extern void discard_insertions __PROTO((void));
  242. extern void start_numbering __PROTO((int at_number));
  243. extern void stop_numbering __PROTO((void));
  244. extern void number_item __PROTO((void));
  245. extern void insert_self __PROTO((void));
  246. extern void cm_null __PROTO((int arg));
  247. extern void cm_asterisk __PROTO((void));
  248. extern void cm_dots __PROTO((int arg));
  249. extern void cm_bullet __PROTO((int arg));
  250. extern void cm_minus __PROTO((int arg));
  251. extern void cm_TeX __PROTO((int arg));
  252. extern void cm_copyright __PROTO((int arg));
  253. extern void cm_code __PROTO((int arg));
  254. /**
  255. extern void cm_samp __PROTO((int arg));
  256. extern void cm_file __PROTO((int arg));
  257. extern void cm_kbd __PROTO((int arg));
  258. extern void cm_key __PROTO((int arg));
  259. **/
  260. extern void cm_ctrl __PROTO((int arg, size_t position));
  261. extern void cm_sc __PROTO((int arg, size_t start_pos, size_t end_pos));
  262. extern void cm_var __PROTO((int arg, size_t start_pos, size_t end_pos));
  263. extern void cm_dfn __PROTO((int arg, size_t position));
  264. extern void cm_emph __PROTO((int arg));
  265. extern void cm_strong __PROTO((int arg, size_t position));
  266. extern void cm_cite __PROTO((int arg, size_t position));
  267. /**
  268. extern void cm_italic __PROTO((int arg));
  269. extern void cm_bold __PROTO((int arg));
  270. extern void cm_roman __PROTO((int arg));
  271. extern void cm_title __PROTO((int arg));
  272. extern void cm_refill __PROTO((int arg));
  273. **/
  274. extern void cm_w __PROTO((int arg));
  275. extern void cm_obsolete __PROTO((int arg));
  276. extern void insert_and_underscore __PROTO((int with_char));
  277. extern void cm_chapter __PROTO((void));
  278. extern void cm_section __PROTO((void));
  279. extern void cm_subsection __PROTO((void));
  280. extern void cm_subsubsection __PROTO((void));
  281. extern void cm_unnumbered __PROTO((void));
  282. extern void cm_unnumberedsec __PROTO((void));
  283. extern void cm_unnumberedsubsec __PROTO((void));
  284. extern void cm_unnumberedsubsubsec __PROTO((void));
  285. extern void cm_appendix __PROTO((void));
  286. extern void cm_appendixsec __PROTO((void));
  287. extern void cm_appendixsubsec __PROTO((void));
  288. extern void cm_appendixsubsubsec __PROTO((void));
  289. extern void cm_majorheading __PROTO((void));
  290. extern void cm_chapheading __PROTO((void));
  291. extern void cm_heading __PROTO((void));
  292. extern void cm_subheading __PROTO((void));
  293. extern void cm_subsubheading __PROTO((void));
  294. extern void init_tag_table __PROTO((void));
  295. extern void write_tag_table __PROTO((void));
  296. extern void write_tag_table_indirect __PROTO((void));
  297. extern void write_tag_table_internal __PROTO((boolean indirect_p));
  298. extern char *get_node_token __PROTO((void));
  299. extern void normalize_node_name __PROTO((char *string));
  300. extern TAG_ENTRY *find_node __PROTO((char *name));
  301. extern void remember_node __PROTO((char *node,
  302.                    char *prev,
  303.                    char *next,
  304.                    char *up,
  305.                    size_t position,
  306.                    size_t line_no,
  307.                    int no_warn));
  308. extern int what_section __PROTO((char *text));
  309. extern void cm_node __PROTO((void));
  310. extern void validate_file __PROTO((char *filename, TAG_ENTRY *tag_table));
  311. extern int validate __PROTO((char *tag, size_t line, char *label));
  312. extern void split_file __PROTO((char *filename, size_t size));
  313. extern char *reftype_type_string __PROTO((enum reftype type));
  314. extern void remember_node_reference __PROTO((char *node,
  315.                          int line,
  316.                          enum reftype type));
  317. extern void validate_other_references __PROTO((NODE_REF *ref_list));
  318. extern NODE_REF *find_node_reference __PROTO((char *node, NODE_REF *ref_list));
  319. extern void free_node_references __PROTO((void));
  320. extern void glean_node_from_menu __PROTO((void));
  321. extern void cm_menu __PROTO((void));
  322. extern char *get_xref_token __PROTO((void));
  323. extern void cm_xref __PROTO((int arg));
  324. extern void cm_pxref __PROTO((int arg));
  325. extern void cm_inforef __PROTO((int arg));
  326. extern void cm_quotation __PROTO((void));
  327. extern void cm_example __PROTO((void));
  328. extern void cm_smallexample __PROTO((void));
  329. extern void cm_lisp __PROTO((void));
  330. extern void cm_format __PROTO((void));
  331. extern void cm_display __PROTO((void));
  332. extern void cm_itemize __PROTO((void));
  333. extern void cm_enumerate __PROTO((void));
  334. extern void cm_table __PROTO((void));
  335. extern void cm_group __PROTO((void));
  336. extern void cm_ifinfo __PROTO((void));
  337. extern void cm_tex __PROTO((void));
  338. extern void cm_iftex __PROTO((void));
  339. extern void cm_titlespec __PROTO((void));
  340. extern void cm_titlepage __PROTO((void));
  341. extern void cm_ignore __PROTO((void));
  342. extern void execute_string __PROTO((const char *format, ...));
  343. extern void cm_itemx __PROTO((void));
  344. extern void cm_item __PROTO((void));
  345. extern char *defun_title __PROTO((enum insertion_type type));
  346. extern char **args_from_string __PROTO((char *string));
  347. extern void get_defun_args __PROTO((void));
  348. extern void insert_defun_arg __PROTO((char *string, int where));
  349. extern void defun_internal __PROTO((enum insertion_type type,
  350.                     char *title, int x_p));
  351. extern void cm_defun __PROTO((void));
  352. extern void cm_end __PROTO((void));
  353. extern void cm_noindent __PROTO((void));
  354. extern void cm_setfilename __PROTO((void));
  355. extern void cm_comment __PROTO((void));
  356. extern void cm_br __PROTO((void));
  357. extern void cm_sp __PROTO((void));
  358. extern void cm_settitle __PROTO((void));
  359. /** extern void cm_need __PROTO((void)); **/
  360. extern void cm_center __PROTO((void));
  361. extern void cm_result __PROTO((int arg));
  362. extern void cm_expansion __PROTO((int arg));
  363. extern void cm_equiv __PROTO((int arg));
  364. extern void cm_print __PROTO((int arg));
  365. extern void cm_error __PROTO((int arg));
  366. extern void cm_point __PROTO((int arg));
  367. extern void cm_exdent __PROTO((void));
  368. extern void cm_include __PROTO((void));
  369. extern void cm_infoinclude __PROTO((void));
  370. extern void misplaced_brace __PROTO((void));
  371. /** extern void cm_force_abbreviated_whitespace __PROTO((void)); **/
  372. extern void cm_force_sentence_end __PROTO((void));
  373. extern void cm_bye __PROTO((void));
  374. /** extern void cm_asis __PROTO((void)); **/
  375. extern void cm_setchapternewpage __PROTO((void));
  376. extern void cm_smallbook __PROTO((void));
  377. extern void init_indices __PROTO((void));
  378. extern int find_index_offset __PROTO((char *name));
  379. extern INDEX_ALIST *find_index __PROTO((char *name));
  380. extern int translate_index __PROTO((char *name));
  381. extern INDEX_ELT *index_list __PROTO((char *name));
  382. extern void free_index __PROTO((INDEX_ELT *index));
  383. extern int undefindex __PROTO((char *name));
  384. extern void defindex __PROTO((char *name, int code));
  385. extern void index_add_arg __PROTO((char *name));
  386. extern void gen_index __PROTO((void));
  387. extern void cm_defindex __PROTO((void));
  388. extern void cm_defcodeindex __PROTO((void));
  389. extern void gen_defindex __PROTO((int code));
  390. extern INDEX_ELT *index_append __PROTO((INDEX_ELT *head, INDEX_ELT *tail));
  391. extern void cm_synindex __PROTO((void));
  392. extern void cm_pindex __PROTO((void));
  393. extern void cm_vindex __PROTO((void));
  394. extern void cm_kindex __PROTO((void));
  395. extern void cm_cindex __PROTO((void));
  396. extern void cm_findex __PROTO((void));
  397. extern void cm_tindex __PROTO((void));
  398. extern int index_element_compare __PROTO((INDEX_ELT **element1,
  399.                       INDEX_ELT **element2));
  400. extern INDEX_ELT **sort_index __PROTO((INDEX_ELT *index));
  401. extern void cm_printindex __PROTO((void));
  402. extern void define_user_command __PROTO((char *name,
  403.                      VFUNCTION *proc,
  404.                      int needs_braces_p));
  405. extern void define_alias __PROTO((char *alias, char *function));
  406. extern void set_footnote_style __PROTO((char *string));
  407. extern void remember_note __PROTO((char *marker, char *note));
  408. extern void free_pending_notes __PROTO((void));
  409. extern void cm_footnote __PROTO((void));
  410. extern void output_pending_notes __PROTO((void));
  411.  
  412.  
  413. extern int in_fixed_width_font;
  414.  
  415. /* Some systems don't declare this function in pwd.h. */
  416. extern struct passwd *getpwnam __PROTO((char *));
  417.  
  418.  
  419. /* **************************************************************** */
  420. /*                                    */
  421. /*                  Global Defines                  */
  422. /*                                    */
  423. /* **************************************************************** */
  424.  
  425. /* Error levels */
  426. #define NO_ERROR 0
  427. #define SYNTAX     2
  428. #define FATAL     4
  429.  
  430. /* How to allocate permanent storage for STRING. */
  431. #define savestring(x) \
  432.   ((char *)strcpy (xmalloc (1 + ((x) ? strlen (x) : 0)), (x) ? (x) : ""))
  433.  
  434. /* C's standard macros don't check to make sure that the characters being
  435.    changed are within range.  So I have to check explicitly. */
  436.  
  437. /* GNU Library doesn't have toupper().  Until GNU gets this fixed, I will
  438.    have to do it. */
  439. #ifndef atarist
  440. #ifndef toupper
  441. #define toupper(c) ((c) - 32)
  442. #endif
  443.  
  444. #define coerce_to_upper(c) ((islower(c) ? toupper(c) : (c)))
  445. #define coerce_to_lower(c) ((isupper(c) ? tolower(c) : (c)))
  446. #else
  447. #define coerce_to_upper(c) ((islower(c) ? _toupper(c) : (c)))
  448. #define coerce_to_lower(c) ((isupper(c) ? _tolower(c) : (c)))
  449. #endif
  450. #define control_character_bit 0x40 /* %01000000, must be off. */
  451. #define meta_character_bit 0x080/* %10000000, must be on.  */
  452. #define CTL(c) ((c) & (~control_character_bit))
  453. #define UNCTL(c) coerce_to_upper(((c)|control_character_bit))
  454. #define META(c) ((c) | (meta_character_bit))
  455. #define UNMETA(c) ((c) & (~meta_character_bit))
  456.  
  457. #ifdef atarist
  458. #define whitespace(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\r'))
  459. #else
  460. #define whitespace(c) (((c) == '\t') || ((c) == ' '))
  461. #endif
  462. #define sentence_ender(c) ((c) == '.' || (c) == '?' || (c) == '!')
  463. /*
  464. #define cr_or_whitespace(c) (((c) == '\t') || ((c) == ' ') || ((c) == '\n'))
  465. */
  466. #define cr_or_whitespace(c) isspace((c))
  467. #define member(c, s) (index (s, c) != NULL)
  468.  
  469. #define COMMAND_PREFIX '@'
  470.  
  471. /* Stuff for splitting large files. */
  472. #define SPLIT_SIZE_THRESHOLD 70000    /* What's good enough for Stallman... */
  473. #define DEFAULT_SPLIT_SIZE 50000/* Is probably good enough for me. */
  474. boolean splitting = true;    /* Always true for now. */
  475.  
  476.  
  477.  
  478. /* **************************************************************** */
  479. /*                                    */
  480. /*                Global Variables                */
  481. /*                                    */
  482. /* **************************************************************** */
  483.  
  484. /* Global pointer to argv[0]. */
  485. char *progname;
  486.  
  487. /* The current input file state. */
  488. char *input_filename;
  489. char *input_text;
  490. size_t size_of_input_text;
  491. size_t input_text_offset;
  492. size_t line_number;
  493.  
  494. #ifndef atarist
  495. #define curchar() input_text[input_text_offset]
  496.  
  497. #define command_char(c) ((!whitespace(c)) && \
  498.               ((c) != '\n') && \
  499.               ((c) != '{'))
  500. #else
  501. #define curchar() input_text[input_text_offset]
  502.  
  503. #define command_char(c) ((!isspace((c))) && ((c) != '{'))
  504. #endif
  505. #define skip_whitespace() while (input_text_offset < size_of_input_text \
  506.                  && whitespace(curchar()))\
  507.   input_text_offset++
  508.  
  509. /* And writing to the output. */
  510.  
  511. /* The output file name. */
  512. char *output_filename, *pretty_output_filename;
  513.  
  514. /* Current output stream. */
  515. FILE *output_stream;
  516.  
  517. /* Position in the output file. */
  518. size_t output_position;
  519.  
  520. /* Output paragraph buffer. */
  521. char *output_paragraph;
  522.  
  523. /* Offset into OUTPUT_PARAGRAPH. */
  524. size_t output_paragraph_offset;
  525.  
  526. /* The output paragraph "cursor" horizontal position. */
  527. int output_column = 0;
  528.  
  529. /* non-zero means output_paragraph contains text. */
  530. boolean paragraph_is_open = false;
  531.  
  532. #define INITIAL_PARAGRAPH_SPACE 5000
  533. size_t paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
  534.  
  535. /* Filling.. */
  536. /* True indicates that filling will take place on long lines. */
  537. boolean filling_enabled = true;
  538.  
  539. /* Non-zero means that words are not to be split, even in long lines.  This
  540.    gets changed for cm_w (). */
  541. int non_splitting_words = 0;
  542.  
  543. /* True indicates that filling a line also indents the new line. */
  544. boolean indented_fill = false;
  545.  
  546. /* The column at which long lines are broken. */
  547. int fill_column = 72;
  548.  
  549. /* The amount of indentation to apply at the start of each line. */
  550. int current_indent = 0;
  551.  
  552. /* The amount of indentation to add at the starts of paragraphs.
  553.    0 means don't change existing indentation at paragraph starts.
  554.    > 0 is amount to indent new paragraphs by.
  555.    < 0 means indent to column zero by removing indentation if necessary.
  556.  
  557.    This is normally zero, but some people prefer paragraph starts to be
  558.    somewhat more indented than paragraph bodies.  A pretty value for
  559.    this is 3. */
  560. int paragraph_start_indent = 3;
  561.  
  562. /* Non-zero means that the use of paragraph_start_indent is inhibited.
  563.    @example uses this to line up the left columns of the example text. */
  564. int inhibit_paragraph_indentation = 0;
  565.  
  566. /* Indentation that is pending insertion.  We have this for hacking lines
  567.    which look blank, but contain whitespace.  We want to treat those as
  568.    blank lines. */
  569. int pending_indent = 0;
  570.  
  571. /* The amount that indentation increases/decreases by. */
  572. int default_indentation_increment = 5;
  573.  
  574. /* True indicates that indentation is temporarily turned off. */
  575. boolean no_indent = true;
  576.  
  577. /* Command name in the process of being hacked. */
  578. char *command;
  579.  
  580. /* The index in our internal command table of the currently
  581.    executing command. */
  582. int command_index;
  583.  
  584. /* A stack of file information records.  If a new file is read in with
  585.    "@input", we remember the old input file state on this stack. */
  586. typedef struct fstack
  587. {
  588.   struct fstack *next;
  589.   char *filename;
  590.   char *text;
  591.   size_t size;
  592.   size_t offset;
  593.   size_t line_number;
  594. } FSTACK;
  595.  
  596. FSTACK *filestack = (FSTACK *) NULL;
  597.  
  598. /* Stuff for nodes. */
  599. /* The current nodes node name */
  600. char *current_node;
  601.  
  602. /* The current nodes section level. */
  603. int current_section = 0;
  604.  
  605. /* The filename of the current input file.  This is never freed. */
  606. char *node_filename = (char *)NULL;
  607.  
  608. /* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a,
  609.    we turn on this flag bit in node-b's tag entry.  This means that when
  610.    it is time to validate node-b, we don't report an additional error
  611.    if there was no "Prev" field. */
  612. #define PREV_ERROR 0x1
  613. #define NEXT_ERROR 0x2
  614. #define UP_ERROR   0x4
  615. #define NO_WARN       0x8
  616.  
  617. TAG_ENTRY *tag_table = (TAG_ENTRY *) NULL;
  618.  
  619. /* Menu reference, *note reference, and validation hacking. */
  620.  
  621. /* The linked list of such structures. */
  622. NODE_REF *node_references = (NODE_REF *) NULL;
  623.  
  624. /* Flag which tells us whether to examine menu lines or not. */
  625. int in_menu = 0;
  626.  
  627. /* Flags controlling the operation of the program. */
  628.  
  629. /* Default is to notify users of bad choices. */
  630. boolean print_warnings = true;
  631.  
  632. /* Default is to check node references. */
  633. boolean validating = true;
  634.  
  635. /* Number of errors that we tolerate on a given fileset. */
  636. int max_error_level = 100;
  637.  
  638. /* Maximum number of references to a single node before complaining. */
  639. int reference_warning_limit = 1000;
  640.  
  641. /* Non-zero means print out information about what is going on when it
  642.    is going on. */
  643. int verbose_mode = 0;
  644.  
  645. /* The list of commands that we hack in texinfo.  Each one
  646.    has an associated function.  When the command is encountered in the
  647.    text, the associated function is called with START as the argument.
  648.    If the function expects arguments in braces, it remembers itself on
  649.    the stack.  When the corresponding close brace is encountered, the
  650.    function is called with END as the argument. */
  651.  
  652. #define START 0
  653. #define END 1
  654.  
  655. typedef struct brace_element
  656. {
  657.   struct brace_element *next;
  658.   VFUNCTION *proc;
  659.   size_t pos, line;
  660. } BRACE_ELEMENT;
  661.  
  662. BRACE_ELEMENT *brace_stack = (BRACE_ELEMENT *) NULL;
  663.  
  664.  
  665. /* Stuff for defining commands on the fly. */
  666. COMMAND **user_command_array = (COMMAND **) NULL;
  667. int user_command_array_len = 0;
  668.  
  669. static COMMAND CommandTable[] = {
  670.   {"!", cm_force_sentence_end, false},
  671.   {"'", insert_self, false},
  672.   {"*", cm_asterisk, false},
  673.   {".", cm_force_sentence_end, false},
  674.   {":", /* cm_force_abbreviated_whitespace */ cm_null, false},
  675.   {"?", cm_force_sentence_end, false},
  676.   {"@", insert_self, false},
  677.   {" ", insert_self, false},
  678.   {EOL, insert_self, false},
  679.   {"\n", insert_self, false},
  680.   {"TeX", cm_TeX, true},
  681.   {"`", insert_self, false},
  682.   {"appendix", cm_appendix, false},
  683.   {"appendixsec", cm_appendixsec, false},
  684.   {"appendixsubsec", cm_appendixsubsec, false},
  685.   {"appendixsubsubsec", cm_appendixsubsubsec, false},
  686.   {"asis", /* cm_asis */ cm_null, true},
  687.   {"b", /* cm_bold */ cm_null, true},
  688.   {"br", cm_br, false},
  689.   {"bullet", cm_bullet, true},
  690.   {"bye", cm_bye, false},
  691.   {"c", cm_comment, false},
  692.   {"center", cm_center, false},
  693.   {"chapheading", cm_chapheading, false},
  694.   {"chapter", cm_chapter, false},
  695.   {"cindex", cm_cindex, false},
  696.   {"cite", cm_cite, true},
  697.   {"code", cm_code, true},
  698.   {"comment", cm_comment, false},
  699.   {"contents", do_nothing, false},
  700.   {"copyright", cm_copyright, true},
  701.   {"ctrl", cm_ctrl, true},
  702.   {"defcodeindex", cm_defcodeindex, false},
  703.   {"defindex", cm_defindex, false},
  704.   {"dfn", cm_dfn, true},
  705.  
  706. /* The `def' commands. */
  707.   {"defun", cm_defun, false},
  708.   {"defunx", cm_defun, false},
  709.   {"defvar", cm_defun, false},
  710.   {"defvarx", cm_defun, false},
  711.   {"defopt", cm_defun, false},
  712.   {"defoptx", cm_defun, false},
  713.   {"deffn", cm_defun, false},
  714.   {"deffnx", cm_defun, false},
  715.   {"defspec", cm_defun, false},
  716.   {"defspecx", cm_defun, false},
  717.   {"defmac", cm_defun, false},
  718.   {"defmacx", cm_defun, false},
  719. /* The end of the `def' commands. */
  720.  
  721.   {"display", cm_display, false},
  722.   {"dots", cm_dots, true},
  723.   {"emph", cm_emph, true},
  724.   {"end", cm_end, false},
  725.   {"enumerate", cm_enumerate, false},
  726.   {"equiv", cm_equiv, true},
  727.   {"error", cm_error, true},
  728.   {"example", cm_example, false},
  729.   {"exdent", cm_exdent, false},
  730.   {"expansion", cm_expansion, true},
  731.   {"file", /* cm_file */ cm_code, true},
  732.   {"findex", cm_findex, false},
  733.   {"format", cm_format, false},
  734.   {"group", cm_group, false},
  735.   {"heading", cm_heading, false},
  736.   {"headings", cm_comment, false},
  737.   {"i", /* cm_italic */ cm_null, true},
  738.   {"iappendix", cm_appendix, false},
  739.   {"iappendixsec", cm_appendixsec, false},
  740.   {"iappendixsubsec", cm_appendixsubsec, false},
  741.   {"iappendixsubsubsec", cm_appendixsubsubsec, false},
  742.   {"ichapter", cm_chapter, false},
  743.   {"ifinfo", cm_ifinfo, false},
  744.   {"iftex", cm_iftex, false},
  745.   {"ignore", cm_ignore, false},
  746.   {"include", cm_include, false},
  747.   {"inforef", cm_inforef, true},
  748.   {"input", cm_include, false},
  749.   {"isection", cm_section, false},
  750.   {"isubsection", cm_subsection, false},
  751.   {"isubsubsection", cm_subsubsection, false},
  752.   {"item", cm_item, false},
  753.   {"itemize", cm_itemize, false},
  754.   {"itemx", cm_itemx, false},
  755.   {"iunnumbered", cm_unnumbered, false},
  756.   {"iunnumberedsec", cm_unnumberedsec, false},
  757.   {"iunnumberedsubsec", cm_unnumberedsubsec, false},
  758.   {"iunnumberedsubsubsec", cm_unnumberedsubsubsec, false},
  759.   {"kbd", /* cm_kbd */ cm_code, true},
  760.   {"key", /* cm_key */ cm_null, true},
  761.   {"kindex", cm_kindex, false},
  762.   {"lisp", cm_lisp, false},
  763.   {"majorheading", cm_majorheading, false},
  764.   {"menu", cm_menu},
  765.   {"minus", cm_minus, true},
  766.   {"need", /* cm_need */ cm_null, false},
  767.   {"node", cm_node, false},
  768.   {"noindent", cm_noindent, false},
  769.   {"page", do_nothing, false},
  770.   {"pindex", cm_pindex, false},
  771.   {"point", cm_point, true},
  772.   {"print", cm_print, true},
  773.   {"printindex", cm_printindex, false},
  774.   {"pxref", cm_pxref, true},
  775.   {"quotation", cm_quotation, false},
  776.   {"r", /* cm_roman */ cm_null, true},
  777.   {"ref", cm_xref, true},
  778.   {"refill", /* cm_refill */ cm_null, false},
  779.   {"result", cm_result, true},
  780.   {"samp", /* cm_samp */ cm_code, true},
  781.   {"sc", cm_sc, true},
  782.   {"section", cm_section, false},
  783.   {"setchapternewpage", cm_setchapternewpage, false},
  784.   {"setfilename", cm_setfilename, false},
  785.   {"settitle", cm_settitle, false},
  786.   {"smallexample", cm_smallexample, false},
  787.   {"smallbook", cm_smallbook, false},
  788.   {"sp", cm_sp, false},
  789.   {"strong", cm_strong, true},
  790.   {"subheading", cm_subheading, false},
  791.   {"subsection", cm_subsection, false},
  792.   {"subsubheading", cm_subsubheading, false},
  793.   {"subsubsection", cm_subsubsection, false},
  794.   {"summarycontents", do_nothing, false},
  795.   {"syncodeindex", cm_synindex, false},
  796.   {"synindex", cm_synindex, false},
  797.   {"t", /* cm_title */ cm_null, true},
  798.   {"table", cm_table, false},
  799.   {"tex", cm_tex, false},
  800.   {"tindex", cm_tindex, false},
  801.   {"titlepage", cm_titlepage, false},
  802.   {"titlespec", cm_titlespec, false},
  803.   {"top", cm_chapter, false},        /* added - mj */
  804.   {"unnumbered", cm_unnumbered, false},
  805.   {"unnumberedsec", cm_unnumberedsec, false},
  806.   {"unnumberedsubsec", cm_unnumberedsubsec, false},
  807.   {"unnumberedsubsubsec", cm_unnumberedsubsubsec, false},
  808.   {"var", cm_var, true},
  809.   {"vindex", cm_vindex, false},
  810.   {"w", cm_w, true},
  811.   {"xref", cm_xref, true},
  812.   {"{", insert_self, false},
  813.   {"}", insert_self, false},
  814.  
  815.   /* Now @include does what this was supposed to. */
  816.   {"infoinclude", cm_infoinclude, false},
  817.   {"footnote", cm_footnote, false}, /* self-arg eater */
  818.  
  819.   {(char *) NULL, (VFUNCTION *) NULL}, false};
  820.  
  821. /* Non-zero means we are running inside of Emacs. */
  822. int in_emacs = 0;
  823.  
  824. #ifndef MAKEINFO_MAJOR
  825. #define MAKEINFO_MAJOR 1
  826. #endif
  827.  
  828. #ifndef MAKEINFO_MINOR
  829. #define MAKEINFO_MINOR 1
  830. #endif
  831.  
  832. int major_version = MAKEINFO_MAJOR;
  833. int minor_version = MAKEINFO_MINOR;
  834.  
  835. struct option long_options[] =
  836. {
  837.   { "no-validate", 0, &validating, false },    /* formerly -nv */
  838.   { "no-warn", 0, &print_warnings, false },    /* formerly -nw */
  839.   { "no-split", 0, &splitting, false },        /* formerly -ns */
  840.   { "verbose", 0, &verbose_mode, 1 },        /* formerly -verbose */
  841.   { "fill-column", 1, 0, 'f' },            /* formerly -fc */
  842.   { "paragraph-indent", 1, 0, 'p' },        /* formerly -pi */
  843.   { "error-limit", 1, 0, 'e' },            /* formerly -el */
  844.   { "reference-limit", 1, 0, 'r' },        /* formerly -rl */
  845.   { "footnote-style", 1, 0, 's' },        /* formerly -ft */
  846.   { "version", 0, 0, 'V' },
  847.   {NULL, 0, NULL, 0}
  848. };
  849.   
  850. /* **************************************************************** */
  851. /*                                    */
  852. /*            Main ()  Start of code              */
  853. /*                                        */
  854. /* **************************************************************** */
  855.  
  856. /* For each file mentioned in the command line, process it, turning
  857.    texinfo commands into wonderfully formatted output text. */
  858. int
  859. main (argc, argv)
  860.      int argc;
  861.      char **argv;
  862. {
  863.   char *t = (char *) getenv ("EMACS");
  864.   int c;
  865.   int ind;
  866.  
  867.   progname = argv[0];
  868.  
  869.   if (t && strcmp (t, "t") == 0)
  870.     in_emacs++;
  871.  
  872.   /* Parse argument flags from the input line. */
  873.   while ((c = getopt_long (argc, argv, "", long_options, &ind)) != EOF)
  874.     {
  875.       if (c == 0 && long_options[ind].flag == 0)
  876.     c = long_options[ind].val;
  877.       switch (c)
  878.     {
  879.     case 'f':
  880.       /* user specified fill_column? */
  881.       if (sscanf (optarg, "%d", &fill_column) != 1)
  882.         usage ();
  883.       break;
  884.  
  885.     case 'p':
  886.       /* User specified paragraph indent (paragraph_start_index)? */
  887.       if (sscanf (optarg, "%d", ¶graph_start_indent) != 1)
  888.         usage ();
  889.       break;
  890.  
  891.     case 'e':
  892.       /* User specified error level? */
  893.       if (sscanf (optarg, "%d", &max_error_level) != 1)
  894.         usage ();
  895.       break;
  896.  
  897.     case 'r':
  898.       /* User specified reference warning limit? */
  899.       if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
  900.         usage ();
  901.       break;
  902.  
  903.     case 's':
  904.       /* User specified footnote style? */
  905.       set_footnote_style (optarg);
  906.       break;
  907.  
  908.     case 'V':        /* Use requested version info? */
  909. #ifdef atarist
  910.       fprintf (stderr, "Makeinfo version %d.%d. (for Atari ST)\n",
  911.            major_version, minor_version);
  912. #else
  913.       fprintf (stderr, "Makeinfo version %d.%d.\n",
  914.            major_version, minor_version);
  915. #endif
  916.       exit (NO_ERROR);
  917.       break;
  918.  
  919.     case '?':
  920.       usage ();
  921.     }
  922.     }
  923.  
  924.   if (optind == argc)
  925.     usage ();
  926.  
  927.   /* Remaining arguments are file names of texinfo files.
  928.      Convert them, one by one. */
  929.   while (optind != argc)
  930.     convert (argv[optind++]);
  931.  
  932.   exit (NO_ERROR);
  933. }
  934.  
  935.  
  936. /* **************************************************************** */
  937. /*                                    */
  938. /*            Generic Utilities                */
  939. /*                                    */
  940. /* **************************************************************** */
  941.  
  942. /* Just like malloc, but kills the program in case of fatal error. */
  943. void *
  944. xmalloc (nbytes)
  945.      size_t nbytes;
  946. {
  947.   char *temp = (char *) malloc (nbytes);
  948.   if (temp == (char *) NULL)
  949.     {
  950.       error ("Virtual memory exhausted! Needed %ld bytes.", nbytes);
  951.       exit (FATAL);
  952.     }
  953.   return (temp);
  954. }
  955.  
  956. /* Like realloc (), but barfs if there isn't enough memory. */
  957. void *
  958. xrealloc (pointer, nbytes)
  959.      void *pointer;
  960.      size_t nbytes;
  961. {
  962.   pointer = (char *) realloc (pointer, nbytes);
  963.   if (!pointer)
  964.     {
  965.       error ("Virtual memory exhausted in realloc ().");
  966.       abort ();
  967.     }
  968.   return (pointer);
  969. }
  970.  
  971. /* Tell the user how to use this program. */
  972. void
  973. usage ()
  974. {
  975.   fprintf (stderr, "Usage: %s [options] texinfo-file...\n\
  976. \n\
  977. This program accepts as input files of texinfo commands and text\n\
  978. and outputs a file suitable for reading with GNU Info.\n\
  979. \n\
  980. The options are:\n\
  981. `+no-validate' to suppress node cross reference validation.\n\
  982. `+no-warn' to suppress warning messages (errors are still output).\n\
  983. `+no-split' to suppress the splitting of large files.\n\
  984. `+verbose' to print information about what is being done.\n\
  985. `+version' to print the version number of Makeinfo.\n\
  986. `+paragraph-indent NUM' to set the paragraph indent to NUM (default %d).\n\
  987. `+fill-column NUM' to set the filling column to NUM (default %d).\n\
  988. `+error-limit NUM' to set the error limit to NUM (default %d).\n\
  989. `+reference-limit NUM' to set the reference warning limit to NUM (default %d).\n\
  990. `+footnote-style STYLE' to set the footnote style to STYLE.  STYLE should\n\
  991.   either be `MN' for `make node', or `BN' for `bottom node'.\n\n",
  992.        progname, paragraph_start_indent,
  993.        fill_column, max_error_level, reference_warning_limit);
  994.   exit (FATAL);
  995. }
  996.  
  997. /* **************************************************************** */
  998. /*                                    */
  999. /*            Manipulating Lists                  */
  1000. /*                                        */
  1001. /* **************************************************************** */
  1002.  
  1003. /* Reverse the chain of structures in LIST.  Output the new head
  1004.    of the chain.  You should always assign the output value of this
  1005.    function to something, or you will lose the chain. */
  1006. GENERIC_LIST *
  1007. reverse_list (list)
  1008.      register GENERIC_LIST *list;
  1009. {
  1010.   register GENERIC_LIST *next;
  1011.   register GENERIC_LIST *prev = (GENERIC_LIST *) NULL;
  1012.  
  1013.   while (list)
  1014.     {
  1015.       next = list->next;
  1016.       list->next = prev;
  1017.       prev = list;
  1018.       list = next;
  1019.     }
  1020.   return (prev);
  1021. }
  1022.  
  1023.  
  1024. /* **************************************************************** */
  1025. /*                                    */
  1026. /*            Pushing and Popping Files               */
  1027. /*                                    */
  1028. /* **************************************************************** */
  1029.  
  1030. /* Find and load the file named FILENAME.  Return a pointer to
  1031.    the loaded file, or NULL if it can't be loaded. */
  1032. char *
  1033. find_and_load (filename)
  1034.      char *filename;
  1035. {
  1036.   struct stat fileinfo;
  1037.   int file, count = 0;
  1038.   char *result = (char *) NULL;
  1039.  
  1040.   if (stat (filename, &fileinfo) != 0) {
  1041.       file = -1;
  1042.       goto error_exit;
  1043.   }
  1044.  
  1045.   file = open (filename, O_RDONLY);
  1046.   if (file < 0)
  1047.     goto error_exit;
  1048.  
  1049.   /* Load the file. */
  1050.   if(NULL == (result = xmalloc (fileinfo.st_size)))
  1051.       goto error_exit;
  1052.  
  1053.   /* VMS stat lies about the st_size value.  The actual number of
  1054.      readable bytes is always less than this value.  The arcane
  1055.      mysteries of VMS/RMS are too much to probe, so this hack
  1056.     suffices to make things work. */
  1057. #if defined (VMS)
  1058.   while ((n = read (file, result+count, fileinfo.st_size)) > 0)
  1059.     count += n;
  1060.   if (n == -1)
  1061. #else
  1062.     count = fileinfo.st_size;
  1063. #ifdef atarist
  1064.     if (_read (file, result, fileinfo.st_size) != fileinfo.st_size)
  1065. #else
  1066.     if (read (file, result, fileinfo.st_size) != fileinfo.st_size)
  1067. #endif
  1068. #endif
  1069.   error_exit:
  1070.     {
  1071.       if (result)
  1072.     free (result);
  1073.       if (file != -1)
  1074.     close (file);
  1075.       return ((char *) NULL);
  1076.     }
  1077.   close (file);
  1078.  
  1079.   /* Set the globals to the new file. */
  1080.   input_text = result;
  1081.   size_of_input_text = fileinfo.st_size;
  1082.   input_filename = savestring (filename);
  1083.   node_filename = savestring (filename);
  1084.   input_text_offset = 0;
  1085.   line_number = 1;
  1086.   return (result);
  1087. }
  1088.  
  1089. /* Save the state of the current input file. */
  1090. void
  1091. pushfile ()
  1092. {
  1093.   FSTACK *newstack = (FSTACK *) xmalloc (sizeof (FSTACK));
  1094.   newstack->filename = input_filename;
  1095.   newstack->text = input_text;
  1096.   newstack->size = size_of_input_text;
  1097.   newstack->offset = input_text_offset;
  1098.   newstack->line_number = line_number;
  1099.   newstack->next = filestack;
  1100.  
  1101.   filestack = newstack;
  1102.   push_node_filename ();
  1103. }
  1104.  
  1105. /* Make the current file globals be what is on top of the file stack. */
  1106. void
  1107. popfile ()
  1108. {
  1109.   extern int executing_string;
  1110.   FSTACK *temp = filestack;
  1111.  
  1112.   if (!filestack)
  1113.     abort ();            /* My fault.  I wonder what I did? */
  1114.  
  1115.   /* Make sure that commands with braces have been satisfied. */
  1116.   if (!executing_string)
  1117.     discard_braces ();
  1118.  
  1119.   /* Get the top of the stack into the globals. */
  1120.   input_filename = filestack->filename;
  1121.   input_text = filestack->text;
  1122.   size_of_input_text = filestack->size;
  1123.   input_text_offset = filestack->offset;
  1124.   line_number = filestack->line_number;
  1125.  
  1126.   /* Pop the stack. */
  1127.   filestack = filestack->next;
  1128.   free (temp);
  1129.   pop_node_filename ();
  1130. }
  1131.  
  1132. /* Flush all open files on the file stack. */
  1133. void
  1134. flush_file_stack ()
  1135. {
  1136.   while (filestack)
  1137.     {
  1138.       free (input_filename);
  1139.       free (input_text);
  1140.       popfile ();
  1141.     }
  1142. }
  1143.  
  1144. int node_filename_stack_index = 0;
  1145. int node_filename_stack_size = 0;
  1146. char **node_filename_stack = (char **)NULL;
  1147.  
  1148. void
  1149. push_node_filename ()
  1150. {
  1151.   if (node_filename_stack_index + 1 > node_filename_stack_size)
  1152.     {
  1153.       if (!node_filename_stack)
  1154.     node_filename_stack =
  1155.       (char **)xmalloc ((node_filename_stack_size += 10)
  1156.                 * sizeof (char *));
  1157.       else
  1158.     node_filename_stack =
  1159.       (char **)xrealloc (node_filename_stack,
  1160.                  (node_filename_stack_size + 10)
  1161.                  * sizeof (char *));
  1162.     }
  1163.  
  1164.   node_filename_stack[node_filename_stack_index] = node_filename;
  1165.   node_filename_stack_index++;
  1166. }
  1167.  
  1168. void
  1169. pop_node_filename ()
  1170. {
  1171.   node_filename = node_filename_stack[--node_filename_stack_index];
  1172. }
  1173.  
  1174. /* Return just the simple part of the filename; i.e. the
  1175.    filename without the path information, or extensions.
  1176.    This conses up a new string. */
  1177. char *
  1178. filename_part (filename)
  1179.      char *filename;
  1180. {
  1181. #ifndef atarist
  1182.   register int i = strlen (filename) - 1;
  1183. #else
  1184.   int i;
  1185.  
  1186.   (void) dos2unx(filename, filename);
  1187.   i = strlen (filename) - 1;
  1188. #endif
  1189.  
  1190.   while (i && filename[i] != '/')
  1191.     i--;
  1192.   if (filename[i] == '/')
  1193.     i++;
  1194.  
  1195. #ifdef REMOVE_OUTPUT_EXTENSIONS
  1196.   result = savestring (&filename[i]);
  1197.  
  1198.   /* See if there is an extension to remove.  If so, remove it. */
  1199.   if (rindex (result, '.'))
  1200.     *(rindex (result, '.')) = '\0';
  1201.   return (result);
  1202. #else
  1203.   return (savestring (&filename[i]));
  1204. #endif /* REMOVE_OUTPUT_EXTENSIONS */
  1205. }
  1206.  
  1207. /* Return the pathname part of filename.  This can be NULL. */
  1208. char *
  1209. pathname_part (filename)
  1210.      char *filename;
  1211. {
  1212.   char *result = (char *) NULL;
  1213.   register int i;
  1214.  
  1215.   filename = expand_filename (filename, "");
  1216.  
  1217.   i = strlen (filename) - 1;
  1218.  
  1219.   while (i && filename[i] != '/')
  1220.     i--;
  1221.   if (filename[i] == '/')
  1222.     i++;
  1223.  
  1224.   if (i)
  1225.     {
  1226.       result = xmalloc (1 + i);
  1227.       strncpy (result, filename, i);
  1228.       result[i] = '\0';
  1229.     }
  1230.   free (filename);
  1231.   return (result);
  1232. }
  1233.  
  1234. /* Return the expansion of FILENAME. */
  1235. char *
  1236. expand_filename (filename, input_name)
  1237.      char *filename, *input_name;
  1238. {
  1239. #ifdef atarist
  1240.   (void) dos2unx(filename, filename);
  1241. #endif
  1242.  
  1243.   filename = full_pathname (filename);
  1244.  
  1245.   if (filename[0] == '.')
  1246.     return (filename);
  1247.  
  1248.   if (filename[0] != '/' && input_name[0] == '/')
  1249.     {
  1250.       int len = strlen (input_name);
  1251.       /* Make it so that relative names work. */
  1252.       char *result = xmalloc (1 + len + strlen (filename));
  1253.       int i = len - 1;
  1254.  
  1255.       strcpy (result, input_name);
  1256.       while (result[i] != '/' && i)
  1257.     i--;
  1258.       if (result[i] == '/')
  1259.     i++;
  1260.       strcpy (&result[i], filename);
  1261.       free (filename);
  1262.       return (result);
  1263.     }
  1264.   return (filename);
  1265. }
  1266.  
  1267. /* Return the full path to FILENAME. */
  1268. char *
  1269. full_pathname (filename)
  1270.      char *filename;
  1271. {
  1272.   int initial_character;
  1273.  
  1274.   if (filename && (initial_character = *filename))
  1275.     {
  1276.       if (initial_character == '/')
  1277.     return (savestring (filename));
  1278.       if (initial_character != '~')
  1279.     {
  1280.       return (savestring (filename));
  1281.     }
  1282.       else
  1283.     {
  1284.       if (filename[1] == '/')
  1285.         {
  1286.          /* Return the concatenation of HOME and the rest of the string. */
  1287.           char *temp_home = (char *) getenv ("HOME");
  1288.           char *temp_name = xmalloc (strlen (&filename[2])
  1289.                      + 1
  1290.                      + temp_home ? strlen (temp_home)
  1291.                      : 0);
  1292.           if (temp_home)
  1293.         strcpy (temp_name, temp_home);
  1294.           strcat (temp_name, &filename[2]);
  1295.           return (temp_name);
  1296.         }
  1297.       else
  1298.         {
  1299.           struct passwd *user_entry;
  1300.           int i, c;
  1301.           char *username = xmalloc (257);
  1302.           char *temp_name;
  1303.  
  1304.           for (i = 1; c = filename[i]; i++)
  1305.         {
  1306.           if (c == '/')
  1307.             break;
  1308.           else
  1309.             username[i - 1] = c;
  1310.         }
  1311.           if (c)
  1312.         username[i - 1] = '\0';
  1313.  
  1314.           user_entry = getpwnam (username);
  1315.  
  1316.           if (!user_entry)
  1317.         return (savestring (filename));
  1318.  
  1319.           temp_name = xmalloc (1 + strlen (user_entry->pw_dir)
  1320.                    + strlen (&filename[i]));
  1321.           strcpy (temp_name, user_entry->pw_dir);
  1322.           strcat (temp_name, &filename[i]);
  1323.           return (temp_name);
  1324.         }
  1325.     }
  1326.     }
  1327.   else
  1328.     {
  1329.       return (savestring (filename));
  1330.     }
  1331. }
  1332.  
  1333. /* **************************************************************** */
  1334. /*                                    */
  1335. /*            Error Handling                    */
  1336. /*                                    */
  1337. /* **************************************************************** */
  1338. #ifdef atarist
  1339. #define STDERR stdout
  1340. #else
  1341. #define STDERR stderr
  1342. #endif
  1343.  
  1344.  
  1345. /* Number of errors encountered. */
  1346. int errors_printed = 0;
  1347.  
  1348. /* Print the last error gotten from the file system. */
  1349. int
  1350. fs_error (filename)
  1351.      char *filename;
  1352. {
  1353.   perror (filename);
  1354.   return ((int) false);
  1355. }
  1356.  
  1357. /* Print an error message, and return false. */
  1358. int
  1359. error (const char *format, ...)
  1360. {
  1361.   va_list args;
  1362.  
  1363.   remember_error ();
  1364.   va_start(args, format);
  1365.   vfprintf (STDERR, format, args);
  1366.   va_end(args);
  1367.   fprintf (STDERR, "\n");
  1368.   return ((int) false);
  1369. }
  1370.  
  1371. /* Just like error (), but print the line number as well. */
  1372. int
  1373. line_error (const char *format, ...)
  1374. {
  1375.   va_list args;
  1376.  
  1377.   remember_error ();
  1378.   fprintf (STDERR, "%s:%ld: ", input_filename, line_number);
  1379.   va_start(args, format);
  1380.   vfprintf (STDERR, format, args);
  1381.   va_end(args);
  1382.   fprintf (STDERR, ".\n");
  1383.   return ((int) false);
  1384. }
  1385.  
  1386. int
  1387. warning (const char *format, ...)
  1388. {
  1389.   va_list args;
  1390.  
  1391.   if (print_warnings)
  1392.     {
  1393.       fprintf (STDERR, "%s:%ld: Warning: ", input_filename, line_number);
  1394.       va_start(args, format);
  1395.       vfprintf (STDERR, format, args);
  1396.       va_end(args);
  1397.       fprintf (STDERR, ".\n");
  1398.     }
  1399.   return ((int) false);
  1400. }
  1401.  
  1402. /* Remember that an error has been printed.  If this is the first
  1403.    error printed, then tell them which program is printing them.
  1404.    If more than max_error_level have been printed, then exit the
  1405.    program. */
  1406. void
  1407. remember_error ()
  1408. {
  1409.   errors_printed++;
  1410.   if (max_error_level && (errors_printed > max_error_level))
  1411.     {
  1412.       fprintf (STDERR, "Too many errors!  Gave up.");
  1413.       flush_file_stack ();
  1414.       cm_bye ();
  1415.     }
  1416. }
  1417.  
  1418.  
  1419. /* **************************************************************** */
  1420. /*                                    */
  1421. /*            Hacking Tokens and Strings            */
  1422. /*                                    */
  1423. /* **************************************************************** */
  1424.  
  1425. /* Return the next token as a string pointer.  We cons the
  1426.    string. */
  1427. char *
  1428. read_token ()
  1429. {
  1430.   size_t i;
  1431.   int character;
  1432.   char *result;
  1433.  
  1434.   /* Hack special case.  If the first character to be read is
  1435.      self-delimiting, then that is the command itself. */
  1436.  
  1437.   character = curchar ();
  1438.   if (self_delimiting (character))
  1439.     {
  1440.       input_text_offset++;
  1441.       result = savestring (" ");
  1442.       *result = character;
  1443.       return (result);
  1444.     }
  1445.  
  1446.   for (i = 0; ((input_text_offset != size_of_input_text)
  1447.            && (character = curchar ())
  1448.            && command_char (character));
  1449.        i++, input_text_offset++);
  1450.   result = xmalloc (i + 1);
  1451.   strncpy (result, &input_text[input_text_offset - i], i);
  1452.   result[i] = '\0';
  1453.   return (result);
  1454. }
  1455.  
  1456. /* Return TRUE if CHARACTER is self-delimiting. */
  1457. boolean
  1458. self_delimiting (character)
  1459.      int character;
  1460. {
  1461. #ifndef atarist
  1462.   return (member (character, "{}:.@*'`,!?; \n"));
  1463. #else
  1464.   return (isspace(character) || member (character, "{}:.@*'`,!?;"));
  1465. #endif
  1466. }
  1467.  
  1468. /* Clear whitespace from the front and end of string. */
  1469. void
  1470. canon_white (string)
  1471.      char *string;
  1472. {
  1473.   int len = strlen (string);
  1474.   int x;
  1475.  
  1476.   if (!len)
  1477.     return;
  1478.  
  1479.   for (x = 0; x < len; x++)
  1480.     {
  1481.       if (!whitespace (string[x]))
  1482.     {
  1483.       strcpy (string, string + x);
  1484.       break;
  1485.     }
  1486.     }
  1487.   len -= x;
  1488.   if (len)
  1489.     len--;
  1490.   while (len > -1 && cr_or_whitespace (string[len]))
  1491.     len--;
  1492.   string[len + 1] = '\0';
  1493. }
  1494.  
  1495. /* Bash STRING, replacing all whitespace with just one space. */
  1496. void
  1497. fix_whitespace (string)
  1498.      char *string;
  1499. {
  1500.   char *temp = xmalloc (strlen (string) + 1);
  1501.   int string_index = 0;
  1502.   int temp_index = 0;
  1503.   int c;
  1504.  
  1505.   canon_white (string);
  1506.  
  1507.   while (string[string_index])
  1508.     {
  1509.       c = temp[temp_index++] = string[string_index++];
  1510.  
  1511. /**
  1512.       if (c == ' ' || c == '\n' || c == '\t')
  1513. **/
  1514.       if (isspace(c))
  1515.     {
  1516.       temp[temp_index - 1] = ' ';
  1517. /**
  1518.       while ((c = string[string_index]) && (c == ' ' ||
  1519.                         c == '\t' ||
  1520.                         c == '\n'))
  1521. **/
  1522.       while ((c = string[string_index]) && (isspace(c)))
  1523.         string_index++;
  1524.     }
  1525.     }
  1526.   temp[temp_index] = '\0';
  1527.   strcpy (string, temp);
  1528.   free (temp);
  1529. }
  1530.  
  1531. /* Discard text until the desired string is found.  The string is
  1532.    included in the discarded text. */
  1533. void
  1534. discard_until (string)
  1535.      char *string;
  1536. {
  1537.   size_t temp = search_forward (string, input_text_offset);
  1538.  
  1539.   size_t len = strlen (string);
  1540.   size_t tt;
  1541.   size_t from = input_text_offset;
  1542.  
  1543.   tt = ((long) temp < 0) ? size_of_input_text : temp + len;
  1544.   /* Find out what line we are on. */
  1545.   while (from != tt)
  1546.     if (input_text[from++] == '\n')
  1547.       line_number++;
  1548.  
  1549.   if ((long)temp < 0)
  1550.     {
  1551.       input_text_offset = size_of_input_text - len;
  1552.  
  1553.       if (strcmp (string, EOL) != 0)
  1554.     {
  1555.       line_error ("Expected `%s'", string);
  1556.       return;
  1557.     }
  1558.     }
  1559.   else
  1560.     input_text_offset = temp;
  1561.  
  1562.   input_text_offset += len;
  1563. }
  1564.  
  1565. /* Read characters from the file until we are at MATCH.
  1566.    Place the characters read into STRING.
  1567.    On exit input_text_offset is after the match string.
  1568.    Return the length of STRING. */
  1569. void
  1570. get_until (match, string)
  1571.      char *match, **string;
  1572. {
  1573.   size_t len;
  1574.   size_t current_point = input_text_offset;
  1575.   size_t x = current_point;
  1576.   size_t new_point = search_forward (match, input_text_offset);
  1577.  
  1578.   if ((long)new_point < 0)
  1579.     new_point = size_of_input_text;
  1580.   len = new_point - current_point;
  1581.  
  1582.   /* Keep track of which line number we are at. */
  1583.   while (x != new_point)
  1584.     if (input_text[x++] == '\n')
  1585.       line_number++;
  1586.  
  1587.   *string = xmalloc (len + 1);
  1588.  
  1589.   strncpy (*string, &input_text[current_point], len);
  1590.   (*string)[len] = '\0';
  1591.  
  1592.   /* Now leave input_text_offset in a consistent state. */
  1593.   input_text_offset = new_point + (strlen (match) - 1);
  1594.   if (input_text_offset > size_of_input_text)
  1595.     input_text_offset = size_of_input_text;
  1596. }
  1597.  
  1598. /* Read characters from the file until we are at MATCH or end of line.
  1599.    Place the characters read into STRING.  */
  1600. void
  1601. get_until_in_line (match, string)
  1602.      char *match, **string;
  1603. {
  1604.   size_t real_bottom = size_of_input_text;
  1605.   size_t temp = search_forward (EOL, input_text_offset);
  1606.   if ((long)temp < 0)
  1607.     temp = size_of_input_text;
  1608.   size_of_input_text = temp;
  1609.   get_until (match, string);
  1610.   canon_white(*string);
  1611.   size_of_input_text = real_bottom;
  1612. }
  1613.  
  1614. void
  1615. get_rest_of_line (string)
  1616.      char **string;
  1617. {
  1618.   get_until (EOL, string);
  1619.   canon_white (*string);
  1620.   if (curchar () == '\n')
  1621.     {                /* as opposed to the end of the file... */
  1622.       line_number++;
  1623.       input_text_offset++;
  1624.     }
  1625. }
  1626.  
  1627. /* Read characters from the file until we are at MATCH or closing brace.
  1628.    Place the characters read into STRING.  */
  1629. void
  1630. get_until_in_braces (match, string)
  1631.      char *match, **string;
  1632. {
  1633.   size_t i, brace = 0;
  1634.   size_t match_len = strlen (match);
  1635.   char *temp;
  1636.  
  1637.   for (i = input_text_offset; i < size_of_input_text; i++)
  1638.     {
  1639.       switch (input_text[i])
  1640.     {
  1641.     case '{':
  1642.         brace++;
  1643.         break;
  1644.     case '}':
  1645.         brace--;
  1646.         break;
  1647.     case '\n':
  1648.         line_number++;
  1649.         break;
  1650.     default:
  1651.         break;
  1652.     }
  1653.       if (((long)brace < 0) ||
  1654.       (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
  1655.     break;
  1656.     }
  1657.   match_len = i - input_text_offset;
  1658.   temp = xmalloc (2 + match_len);
  1659.   strncpy (temp, input_text + input_text_offset, match_len);
  1660.   temp[match_len] = '\0';
  1661.   input_text_offset = i;
  1662.   *string = temp;
  1663. }
  1664.  
  1665. /* **************************************************************** */
  1666. /*                                    */
  1667. /*            Converting the File                 */
  1668. /*                                    */
  1669. /* **************************************************************** */
  1670.  
  1671. /* Convert the file named by NAME.  The output is saved on the file
  1672.    named as the argument to the @setfilename command. */
  1673. void
  1674. convert (name)
  1675.      char *name;
  1676. {
  1677.   char *real_output_filename = NULL;
  1678.  
  1679.   init_tag_table ();
  1680.   init_indices ();
  1681.   init_internals ();
  1682.   init_paragraph ();
  1683.   if (!find_and_load (name))
  1684.     {
  1685.       /* For some reason, the file couldn't be loaded.  Print a message
  1686.      to that affect, and split. */
  1687.       fs_error (name);
  1688.       return;
  1689.     }
  1690.   else
  1691.     input_filename = savestring (name);
  1692.  
  1693.   /* Search this file looking for the special string which starts conversion.
  1694.      Once found, we may truly begin. */
  1695.  
  1696.   input_text_offset = search_forward ("@setfilename", 0);
  1697.   if ((long)input_text_offset < 0)
  1698.     {
  1699.       error ("No `@setfilename' found in `%s'", name);
  1700.       goto finished;
  1701.     }
  1702.   else
  1703.     input_text_offset += sizeof("@setfilename");
  1704.  
  1705.   get_until (EOL, &output_filename);    /* no braces expected. */
  1706.   canon_white (output_filename);
  1707.  
  1708.   printf ("Making info file `%s' from `%s'.\n", output_filename, name);
  1709.   real_output_filename = expand_filename (output_filename, name);
  1710.   output_stream = fopen (real_output_filename, "w");
  1711.   if (output_stream == NULL)
  1712.     {
  1713.       fs_error (real_output_filename);
  1714.       goto finished;
  1715.     }
  1716.  
  1717.   /* Make the displayable filename from output_filename.  Only the root
  1718.      portion of the filename need be displayed. */
  1719.   pretty_output_filename = filename_part (output_filename);
  1720.  
  1721.   /* For this file only, count the number of newlines from the top of
  1722.      the file to here.  This way, we keep track of line numbers for
  1723.      error reporting.  Line_number starts at 1, since the user isn't
  1724.      zero-based. */
  1725.   {
  1726.     int temp = 0;
  1727.     line_number = 1;
  1728.     while (temp != input_text_offset)
  1729.       if (input_text[temp++] == '\n')
  1730.     line_number++;
  1731.   }
  1732.  
  1733.   add_word_args ("Info file %s, produced by Makeinfo, -*- Text -*-\n\
  1734. from input file %s.\n", output_filename, input_filename);
  1735.   close_paragraph ();
  1736.  
  1737.   reader_loop ();
  1738.  
  1739. finished:
  1740.   close_paragraph ();
  1741.   flush_file_stack ();
  1742.   if (output_stream != NULL)
  1743.     {
  1744.       output_pending_notes ();
  1745.       free_pending_notes ();
  1746.       if (tag_table != NULL)
  1747.     {
  1748.       tag_table = (TAG_ENTRY *) reverse_list ((GENERIC_LIST *)tag_table);
  1749.       write_tag_table ();
  1750.     }
  1751.  
  1752.       fclose (output_stream);
  1753.  
  1754.       if (NULL != real_output_filename)
  1755.          {
  1756.         /* If validating, then validate the entire file right now. */
  1757.         if (validating)
  1758.         validate_file (real_output_filename, tag_table);
  1759.          
  1760.         /* This used to test  && !errors_printed.
  1761.            But some files might have legit warnings.  So split anyway.  */
  1762.         if (splitting)
  1763.         split_file (real_output_filename, 0);
  1764.      }
  1765.     }
  1766. }
  1767.  
  1768. void
  1769. free_and_clear (pointer)
  1770.      char **pointer;
  1771. {
  1772.   if ((*pointer) != (char *) NULL)
  1773.     {
  1774.       free (*pointer);
  1775.       *pointer = (char *) NULL;
  1776.     }
  1777. }
  1778.  
  1779.  /* Initialize some state. */
  1780. void
  1781. init_internals ()
  1782. {
  1783.   free_and_clear (¤t_node);
  1784.   free_and_clear (&output_filename);
  1785.   free_and_clear (&command);
  1786.   free_and_clear (&input_filename);
  1787.   free_node_references ();
  1788.   init_insertion_stack ();
  1789.   init_brace_stack ();
  1790.   command_index = 0;
  1791.   in_menu = 0;
  1792. }
  1793.  
  1794. void
  1795. init_paragraph ()
  1796. {
  1797.   free_and_clear (&output_paragraph);
  1798.   output_paragraph = xmalloc (paragraph_buffer_len);
  1799.   output_position = 0;
  1800.   output_paragraph[0] = '\0';
  1801.   output_paragraph_offset = 0;
  1802.   output_column = 0;
  1803.   paragraph_is_open = false;
  1804.   current_indent = 0;
  1805. }
  1806.  
  1807. /* Okay, we are ready to start the conversion.  Call the reader on
  1808.    some text, and fill the text as it is output.  Handle commands by
  1809.    remembering things like open braces and the current file position on a
  1810.    stack, and when the corresponding close brace is found, you can call
  1811.    the function with the proper arguments. */
  1812. void
  1813. reader_loop ()
  1814. {
  1815.   int character;
  1816.   boolean done = false;
  1817.   int dash_count = 0;
  1818.  
  1819.   while (!done)
  1820.     {
  1821.       if (input_text_offset >= size_of_input_text)
  1822.     {
  1823.       if (filestack)
  1824.         {
  1825.           free (input_filename);
  1826.           free (input_text);
  1827.           popfile ();
  1828.         }
  1829.       else
  1830.         break;
  1831.     }
  1832.       character = curchar ();
  1833.  
  1834.       if (!in_fixed_width_font &&
  1835.       (character == '\'' || character == '`') &&
  1836.       input_text[input_text_offset + 1] == character)
  1837.     {
  1838.       input_text_offset++;
  1839.       character = '"';
  1840.     }
  1841.  
  1842.       if (character == '-')
  1843.     {
  1844.       dash_count++;
  1845.       if (dash_count == 3 && !in_fixed_width_font)
  1846.         {
  1847.           input_text_offset++;
  1848.           continue;
  1849.         }
  1850.     }
  1851.       else
  1852.     {
  1853.       dash_count = 0;
  1854.     }
  1855.  
  1856.       if (character == '\n')
  1857.     {
  1858.       line_number++;
  1859. #ifdef DOTS
  1860.       if (0 == (line_number & 0x3fL))
  1861.           fprintf(stderr, ".");
  1862. #endif
  1863.       if (in_menu && input_text_offset + 1 < size_of_input_text)
  1864.         {
  1865.           glean_node_from_menu ();
  1866.         }
  1867.  
  1868.       /* If the following line is all whitespace, advance to the carriage
  1869.          return on it. */
  1870.       {
  1871.         register size_t i = input_text_offset + 1;
  1872.         
  1873.         while (i < size_of_input_text && whitespace (input_text[i]))
  1874.           i++;
  1875.         
  1876.             if (i == size_of_input_text || input_text[i] == '\n')
  1877.           input_text_offset = i - 1;
  1878.       }
  1879.     }
  1880.       
  1881.       switch (character)
  1882.     {
  1883.     case COMMAND_PREFIX:
  1884.       read_command ();
  1885.       if (strcmp (command, "bye") == 0)
  1886.         {
  1887.           done = true;
  1888.           continue;
  1889.         }
  1890.       break;
  1891.  
  1892.     case '{':
  1893.  
  1894.       /* Special case.  I'm not supposed to see this character by itself.
  1895.          If I do, it means there is a syntax error in the input text.
  1896.          Report the error here, but remember this brace on the stack so
  1897.          you can ignore its partner. */
  1898.  
  1899.       line_error ("Misplaced `{'");
  1900.       remember_brace (misplaced_brace);
  1901.  
  1902.       /* Don't advance input_text_offset since this happens in
  1903.          remember_brace ().
  1904.          input_text_offset++;
  1905.            */
  1906.       break;
  1907.  
  1908.     case '}':
  1909.       pop_and_call_brace ();
  1910.       input_text_offset++;
  1911.       break;
  1912.  
  1913.     default:
  1914.       add_char (character);
  1915.       input_text_offset++;
  1916.     }
  1917.     }
  1918. }
  1919.  
  1920. /* Find the command corresponding to STRING.  If the command
  1921.    is found, return a pointer to the data structure.  Otherwise
  1922.    return (-1). */
  1923. COMMAND *
  1924. get_command_entry (string)
  1925.      char *string;
  1926. {
  1927.   register int i;
  1928.  
  1929.   for (i = 0; CommandTable[i].name; i++)
  1930.     if (strcmp (CommandTable[i].name, string) == 0)
  1931.       return (&CommandTable[i]);
  1932.  
  1933.   /* This command is not in our predefined command table.  Perhaps
  1934.      it is a user defined command. */
  1935.   for (i = 0; i < user_command_array_len; i++)
  1936.     if (user_command_array[i] &&
  1937.     (strcmp (user_command_array[i]->name, string) == 0))
  1938.       return (user_command_array[i]);
  1939.  
  1940.   /* Nope, we never heard of this command. */
  1941.   return ((COMMAND *) - 1);
  1942. }
  1943.  
  1944. /* input_text_offset is right at the command prefix character.
  1945.    Read the next token to determine what to do. */
  1946. void
  1947. read_command ()
  1948. {
  1949.   COMMAND *entry;
  1950.   input_text_offset++;
  1951.   free_and_clear (&command);
  1952.   command = read_token ();
  1953.  
  1954.   entry = get_command_entry (command);
  1955.   if ((long) entry < 0)
  1956.     {
  1957.       line_error ("Unknown info command `%s'", command);
  1958.       return;
  1959.     }
  1960.  
  1961.   if (entry->argument_in_braces)
  1962.     remember_brace (entry->proc);
  1963.  
  1964.   (*((void (*)(int))(entry->proc))) (START);
  1965. }
  1966.  
  1967. /* Return the string which invokes PROC; a pointer to a function. */
  1968. char *
  1969. find_proc_name (proc)
  1970.      FUNCTION *proc;
  1971. {
  1972.   register int i;
  1973.  
  1974.   for (i = 0; CommandTable[i].name; i++)
  1975.     if (proc == CommandTable[i].proc)
  1976.       return (CommandTable[i].name);
  1977.   return ("NO_NAME!");
  1978. }
  1979.  
  1980. void
  1981. init_brace_stack ()
  1982. {
  1983.   brace_stack = (BRACE_ELEMENT *) NULL;
  1984. }
  1985.  
  1986. void
  1987. remember_brace (proc)
  1988.      VFUNCTION *proc;
  1989. {
  1990.   if (curchar () != '{')
  1991.     line_error ("@%s expected `{..}'", command);
  1992.   else
  1993.     input_text_offset++;
  1994.   remember_brace_1 (proc, output_paragraph_offset);
  1995. }
  1996.  
  1997. /* Remember the current output position here.  Save PROC
  1998.    along with it so you can call it later. */
  1999. void
  2000. remember_brace_1 (proc, position)
  2001.      FUNCTION *proc;
  2002.      size_t position;
  2003. {
  2004.   BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT));
  2005.   new->next = brace_stack;
  2006.   new->proc = proc;
  2007.   new->pos = position;
  2008.   new->line = line_number;
  2009.   brace_stack = new;
  2010. }
  2011.  
  2012. /* Pop the top of the brace stack, and call the associated function
  2013.    with the args END and POS. */
  2014. int
  2015. pop_and_call_brace ()
  2016. {
  2017.   BRACE_ELEMENT *temp;
  2018.   FUNCTION *proc;
  2019.   size_t pos;
  2020.  
  2021.   if (brace_stack == (BRACE_ELEMENT *) NULL)
  2022.     return (line_error ("Unmatched close bracket"));
  2023.  
  2024.   pos = brace_stack->pos;
  2025.   proc = brace_stack->proc;
  2026.   temp = brace_stack->next;
  2027.   free (brace_stack);
  2028.   brace_stack = temp;
  2029.  
  2030.   return ((* ((int (*)(int, size_t, size_t))proc))
  2031.       (END, pos, output_paragraph_offset));
  2032. }
  2033.  
  2034. /* You call discard_braces () when you shouldn't have any braces on the stack.
  2035.    I used to think that this happens for commands that don't take arguments
  2036.    in braces, but that was wrong because of things like @code{foo @@}.  So now
  2037.    I only detect it at the beginning of nodes. */
  2038. void
  2039. discard_braces ()
  2040. {
  2041.   size_t temp_line_number = line_number;
  2042.   char *proc_name;
  2043.  
  2044.   if (!brace_stack)
  2045.     return;
  2046.  
  2047.   while (brace_stack)
  2048.     {
  2049.       line_number = brace_stack->line;
  2050.       proc_name = find_proc_name (brace_stack->proc);
  2051.       line_error ("@%s missing close brace", proc_name);
  2052.       line_number = temp_line_number;
  2053.       pop_and_call_brace ();
  2054.     }
  2055. }
  2056.  
  2057. int
  2058. get_char_len (character)
  2059.      int character;
  2060. {
  2061.   /* Return the printed length of the character. */
  2062.   int len;
  2063.  
  2064.   switch (character)
  2065.     {
  2066.     case '\t':
  2067.       len = (output_column + 8) & 0xf7;
  2068.       if (len > fill_column)
  2069.     len = fill_column - output_column;
  2070.       else
  2071.     len = len - output_column;
  2072.       break;
  2073.  
  2074.     case '\n':
  2075.       len = fill_column - output_column;
  2076.       break;
  2077.  
  2078. #ifdef atarist
  2079.     case '\r':
  2080.       len = 0;
  2081.       break;
  2082. #endif
  2083.  
  2084.     default:
  2085.       if (character < ' ')
  2086.     len = 2;
  2087.       else
  2088.     len = 1;
  2089.     }
  2090.   return (len);
  2091. }
  2092.  
  2093. void
  2094. add_word_args (const char *format, ...)
  2095. {
  2096.   va_list args;
  2097.  
  2098.   char buffer[1000];
  2099.   va_start(args, format);
  2100.   vsprintf (buffer, format, args);
  2101.   va_end(args);
  2102.   add_word (buffer);
  2103. }
  2104.  
  2105. /* Add STRING to output_paragraph. */
  2106. void
  2107. add_word (string)
  2108.      char *string;
  2109. {
  2110.   while (*string)
  2111.     add_char (*string++);
  2112. }
  2113.  
  2114. boolean last_char_was_newline = true;
  2115. int last_inserted_character = 0;
  2116.  
  2117. /* Add the character to the current paragraph.  If filling_enabled is
  2118.    true, then do filling as well. */
  2119. void
  2120. add_char (character)
  2121.      int character;
  2122. {
  2123.   extern int must_start_paragraph;
  2124.  
  2125.   /* we are going totally ignore all '\r's */
  2126.   if ('\r' == character)
  2127.     return;        /* ignore it */
  2128.   /* If we are adding a character now, then we don't have to
  2129.      ignore close_paragraph () calls any more. */
  2130.   if (must_start_paragraph)
  2131.     {
  2132.       must_start_paragraph = 0;
  2133.       if (current_indent > output_column)
  2134.     {
  2135.       indent (current_indent - output_column);
  2136.       output_column = current_indent;
  2137.     }
  2138.     }
  2139.  
  2140. /**
  2141.   if (non_splitting_words && member (character, " \t\n"))
  2142. **/
  2143.   if (non_splitting_words && isspace(character))
  2144.     character = ' ' | 0x80;
  2145.  
  2146.   switch (character)
  2147.     {
  2148.     case '\n':
  2149.       if (!filling_enabled)
  2150.     {
  2151.       insert ('\n');
  2152.  
  2153.       /* Should I be flushing output here? * /
  2154.           flush_output (); */
  2155.  
  2156.       output_column = 0;
  2157.       if (!no_indent)
  2158.         indent (output_column = current_indent);
  2159.       break;
  2160.     }
  2161.       else
  2162.     {
  2163.       if (sentence_ender (last_inserted_character))
  2164.         {
  2165.           insert (' ');
  2166.           output_column++;
  2167.           last_inserted_character = character;
  2168.         }
  2169.     }
  2170.  
  2171.       if (last_char_was_newline)
  2172.     {
  2173.       close_paragraph ();
  2174.       pending_indent = 0;
  2175.     }
  2176.       else
  2177.     {
  2178.       last_char_was_newline = true;
  2179.       insert (' ');
  2180.       output_column++;
  2181.     }
  2182.       break;
  2183.  
  2184.     default:
  2185.       {
  2186.     int len = get_char_len (character);
  2187.     if ((character == ' ') && (last_char_was_newline))
  2188.       {
  2189.         if (!paragraph_is_open)
  2190.           {
  2191.         pending_indent++;
  2192.         return;
  2193.           }
  2194.       }
  2195.     if (!paragraph_is_open)
  2196.       {
  2197.         start_paragraph ();
  2198.  
  2199.         /* If the paragraph is supposed to be indented a certain way,
  2200.            then discard all of the pending whitespace.  Otherwise, we
  2201.            let the whitespace stay. */
  2202.         if (!paragraph_start_indent)
  2203.           indent (pending_indent);
  2204.         pending_indent = 0;
  2205.       }
  2206.     if ((output_column += len) >= fill_column)
  2207.       {
  2208.         if (filling_enabled)
  2209.           {
  2210.         size_t temp = output_paragraph_offset - 1;
  2211.         while (temp > 0 && output_paragraph[--temp] != '\n')
  2212.           {
  2213.             if (output_paragraph[temp] == ' ')
  2214.               {
  2215.             output_paragraph[temp++] = '\n';
  2216.  
  2217.             /* We have correctly broken the line where we want
  2218.                to.  What we don't want is spaces following where
  2219.                we have decided to break the line.  We get rid of
  2220.                them. */
  2221.             {
  2222.               size_t t1 = temp;
  2223.               while (t1 < output_paragraph_offset
  2224.                  && whitespace (output_paragraph[t1]))
  2225.                 t1++;
  2226.  
  2227.               if (t1 != temp)
  2228.                 {
  2229.                   strncpy (&output_paragraph[temp],
  2230.                        &output_paragraph[t1],
  2231.                        (output_paragraph_offset - t1));
  2232.                   output_paragraph_offset -= (t1 - temp);
  2233.                 }
  2234.             }
  2235.  
  2236.             /* Filled, but now indent if that is right. */
  2237.             if (indented_fill && current_indent)
  2238.               {
  2239.                 size_t buffer_len =
  2240.                 ((output_paragraph_offset - temp)
  2241.                  + current_indent);
  2242.                 char *temp_buffer = xmalloc (buffer_len);
  2243.                 int indentation = 0;
  2244.  
  2245.                 /* We have to shift any markers that are in
  2246.                    front of the wrap point. */
  2247.                 {
  2248.                   register BRACE_ELEMENT *stack = brace_stack;
  2249.  
  2250.                   while (stack)
  2251.                 {
  2252.                   if (stack->pos > temp)
  2253.                     stack->pos += current_indent;
  2254.                   stack = stack->next;
  2255.                 }
  2256.                 }
  2257.  
  2258.                 while (indentation != current_indent)
  2259.                   temp_buffer[indentation++] = ' ';
  2260.  
  2261.                 strncpy (&temp_buffer[current_indent],
  2262.                      &output_paragraph[temp],
  2263.                      buffer_len - current_indent);
  2264.  
  2265.                 if (output_paragraph_offset + buffer_len
  2266.                 >= paragraph_buffer_len)
  2267.                   {
  2268.                 char *tt =
  2269.                 (char *) xrealloc (output_paragraph,
  2270.                       (paragraph_buffer_len += buffer_len));
  2271.                 output_paragraph = tt;
  2272.                   }
  2273.                 strncpy (&output_paragraph[temp],
  2274.                      temp_buffer, buffer_len);
  2275.                 output_paragraph_offset += current_indent;
  2276.                 free (temp_buffer);
  2277.               }
  2278.             output_column = 0;
  2279.             while (temp != output_paragraph_offset)
  2280.               output_column +=
  2281.                   get_char_len (output_paragraph[temp++]);
  2282.             output_column += len;
  2283.             break;
  2284.               }
  2285.           }
  2286.           }
  2287.       }
  2288.     insert (character);
  2289.     last_char_was_newline = false;
  2290.     last_inserted_character = character;
  2291.       }
  2292.     }
  2293. }
  2294.  
  2295. /* Insert CHARACTER into OUTPUT_PARAGRAPH. */
  2296. void
  2297. insert (character)
  2298.      int character;
  2299. {
  2300.   output_paragraph[output_paragraph_offset++] = character;
  2301.   if (output_paragraph_offset == paragraph_buffer_len)
  2302.     {
  2303.       output_paragraph =
  2304.     (char *) xrealloc (output_paragraph,
  2305.               (paragraph_buffer_len += 100));
  2306.     }
  2307. }
  2308.  
  2309. /* Remove upto COUNT characters of whitespace from the
  2310.    the current output line.  If COUNT is less than zero,
  2311.    then remove until none left. */
  2312. void
  2313. kill_self_indent (count)
  2314.      int count;
  2315. {
  2316.   /* Handle infinite case first. */
  2317.   if (count < 0)
  2318.     {
  2319.       output_column = 0;
  2320.       while (output_paragraph_offset)
  2321.     {
  2322.       if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  2323.         output_paragraph_offset--;
  2324.       else
  2325.         break;
  2326.     }
  2327.     }
  2328.   else
  2329.     {
  2330.       while (output_paragraph_offset && count--)
  2331.     if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  2332.       output_paragraph_offset--;
  2333.     else
  2334.       break;
  2335.     }
  2336. }
  2337.  
  2338. void
  2339. flush_output ()
  2340. {
  2341.   register size_t i;
  2342.  
  2343.   if (!output_paragraph_offset)
  2344.     return;
  2345.   for (i = 0; i < output_paragraph_offset; i++)
  2346.     output_paragraph[i] &= 0x7f;
  2347.  
  2348.   fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
  2349.   output_position += output_paragraph_offset;
  2350.   output_paragraph_offset = 0;
  2351. }
  2352.  
  2353. /* How to close a paragraph controlling the number of lines between
  2354.    this one and the last one. */
  2355.  
  2356. /* Paragraph spacing is controlled by this variable.  It is the number of
  2357.    blank lines that you wish to appear between paragraphs.  A value of
  2358.    1 creates a single blank line between paragraphs. */
  2359. int paragraph_spacing = 1;
  2360.  
  2361.  
  2362. /* Close the current paragraph, leaving no blank lines between them. */
  2363. void
  2364. close_single_paragraph ()
  2365. {
  2366.   close_paragraph_with_lines (0);
  2367. }
  2368.  
  2369. void
  2370. close_paragraph_with_lines (lines)
  2371.      int lines;
  2372. {
  2373.   int old_spacing = paragraph_spacing;
  2374.   paragraph_spacing = lines;
  2375.   close_paragraph ();
  2376.   paragraph_spacing = old_spacing;
  2377. }
  2378.  
  2379. /* Non-zero means that start_paragraph () MUST be called before we pay
  2380.    any attention to close_paragraph () calls. */
  2381. int must_start_paragraph = 0;
  2382.  
  2383. /* Close the currently open paragraph. */
  2384. void
  2385. close_paragraph ()
  2386. {
  2387.   if (paragraph_is_open && !must_start_paragraph)
  2388.     {
  2389.       /* Gobble up blank lines that are extra... */
  2390.       register size_t tindex = output_paragraph_offset;
  2391.       register int c;
  2392.       while (tindex &&
  2393.          ((c = output_paragraph[tindex - 1]) == ' ' || c == '\n'))
  2394.     output_paragraph[--tindex] = '\n';
  2395.  
  2396.       output_paragraph_offset = tindex;
  2397.  
  2398.       insert ('\n');
  2399.       {
  2400.     register int i;
  2401.     for (i = 0; i < paragraph_spacing; i++)
  2402.       insert ('\n');
  2403.       }
  2404.       flush_output ();
  2405.       paragraph_is_open = false;
  2406.       no_indent = false;
  2407.     }
  2408.   last_char_was_newline = true;
  2409. }
  2410.  
  2411. /* Begin a new paragraph. */
  2412. void
  2413. start_paragraph ()
  2414. {
  2415.   close_paragraph ();        /* First close existing one. */
  2416.  
  2417.   paragraph_is_open = true;
  2418.  
  2419.   if (!must_start_paragraph)
  2420.     {
  2421.       output_column = 0;
  2422.  
  2423.       /* If doing indentation, then insert the appropriate amount. */
  2424.       if (!no_indent)
  2425.     {
  2426.       if (inhibit_paragraph_indentation || paragraph_start_indent < 0)
  2427.         output_column = current_indent;
  2428.       else
  2429.         output_column = current_indent + paragraph_start_indent;
  2430.  
  2431.       indent (output_column);
  2432.     }
  2433.     }
  2434.   else
  2435.     must_start_paragraph = 0;
  2436. }
  2437.  
  2438. /* Insert the indentation specified by AMOUNT. */
  2439. void
  2440. indent (amount)
  2441.      int amount;
  2442. {
  2443.   while (--amount >= 0)
  2444.     insert (' ');
  2445. }
  2446.  
  2447. /* Search forward for STRING in input_text.
  2448.    FROM says where where to start. */
  2449. size_t
  2450. search_forward (string, from)
  2451.      char *string;
  2452.      size_t from;
  2453. {
  2454.   size_t len = strlen (string);
  2455.  
  2456.   while (from < size_of_input_text)
  2457.     {
  2458.       if (strnicmp (input_text + from, string, len) == 0)
  2459.     return (from);
  2460.       from++;
  2461.     }
  2462.   return (-1);
  2463. }
  2464.  
  2465. /* Whoops, Unix doesn't have stricmp, or strnicmp. */
  2466.  
  2467. /* Case independent string compare. */
  2468. int
  2469. stricmp (string1, string2)
  2470.      char *string1, *string2;
  2471. {
  2472.   char ch1, ch2;
  2473.  
  2474.   for (;;)
  2475.     {
  2476.       ch1 = *string1++;
  2477.       ch2 = *string2++;
  2478.       if (!(ch1 | ch2))
  2479.     return (0);
  2480.  
  2481.       ch1 = coerce_to_upper (ch1);
  2482.       ch2 = coerce_to_upper (ch2);
  2483.  
  2484.       if (ch1 != ch2)
  2485.     return (1);
  2486.     }
  2487. }
  2488.  
  2489. /* Compare at most COUNT characters from string1 to string2.  Case
  2490.    doesn't matter. */
  2491. int
  2492. strnicmp (string1, string2, count)
  2493.      char *string1, *string2;
  2494.      size_t count;
  2495. {
  2496.   char ch1, ch2;
  2497.  
  2498.   while (count)
  2499.     {
  2500.       ch1 = *string1++;
  2501.       ch2 = *string2++;
  2502.       if (coerce_to_upper (ch1) == coerce_to_upper (ch2))
  2503.     count--;
  2504.       else
  2505.     break;
  2506.     }
  2507.   return (count);
  2508. }
  2509.  
  2510. char *insertion_type_names[] = {
  2511.       "menu", "quotation", "lisp", "example", "smallexample", "display",
  2512.       "itemize", "format", "enumerate", "table", "group", "ifinfo",
  2513.       "defun", "defvar", "defopt", "deffn", "defspec", "defmac",
  2514. };
  2515.  
  2516. int insertion_level = 0;
  2517. typedef struct istack_elt
  2518. {
  2519.   struct istack_elt *next;
  2520.   char *item_function;
  2521.   int line_number;
  2522.   int filling_enabled;
  2523.   int indented_fill;
  2524.   enum insertion_type insertion;
  2525.   int inhibited;
  2526. } INSERTION_ELT;
  2527.  
  2528. INSERTION_ELT *insertion_stack = (INSERTION_ELT *) NULL;
  2529.  
  2530. void
  2531. init_insertion_stack ()
  2532. {
  2533.   insertion_stack = (INSERTION_ELT *) NULL;
  2534. }
  2535.  
  2536. /* Return the type of the current insertion. */
  2537. enum insertion_type
  2538. current_insertion_type ()
  2539. {
  2540.   if (!insertion_level)
  2541.     return (bad_type);
  2542.   else
  2543.     return (insertion_stack->insertion);
  2544. }
  2545.  
  2546. /* Return a pointer to the string which is the function
  2547.    to wrap around items. */
  2548. char *
  2549. current_item_function ()
  2550. {
  2551.   if (!insertion_level)
  2552.     return ((char *) NULL);
  2553.   else
  2554.     return (insertion_stack->item_function);
  2555. }
  2556.  
  2557. char *
  2558. get_item_function ()
  2559. {
  2560.   char *item_function;
  2561.   get_until (EOL, &item_function);
  2562.   canon_white (item_function);
  2563.   return (item_function);
  2564. }
  2565.  
  2566.  /* Push the state of the current insertion on the stack. */
  2567. void
  2568. push_insertion (type, item_function)
  2569.      enum insertion_type type;
  2570.      char *item_function;
  2571. {
  2572.   INSERTION_ELT *new = (INSERTION_ELT *) xmalloc (sizeof (INSERTION_ELT));
  2573.  
  2574.   new->item_function = item_function;
  2575.   new->filling_enabled = filling_enabled;
  2576.   new->indented_fill = indented_fill;
  2577.   new->insertion = type;
  2578.   new->line_number = line_number;
  2579.   new->inhibited = inhibit_paragraph_indentation;
  2580.   new->next = insertion_stack;
  2581.   insertion_stack = new;
  2582.   insertion_level++;
  2583. }
  2584.  
  2585.  /* Pop the value on top of the insertion stack into the
  2586.     global variables. */
  2587. void
  2588. pop_insertion ()
  2589. {
  2590.   INSERTION_ELT *temp = insertion_stack;
  2591.   if (temp == (INSERTION_ELT *) NULL)
  2592.     return;
  2593.   inhibit_paragraph_indentation = temp->inhibited;
  2594.   filling_enabled = insertion_stack->filling_enabled;
  2595.   indented_fill = insertion_stack->indented_fill;
  2596.   free_and_clear (&(temp->item_function));
  2597.   insertion_stack = insertion_stack->next;
  2598.   free (temp);
  2599.   insertion_level--;
  2600. }
  2601.  
  2602.  /* Return a pointer to the print name of this
  2603.     enumerated type. */
  2604. char *
  2605. insertion_type_pname (type)
  2606.      enum insertion_type type;
  2607. {
  2608.   if ((int) type < (int) bad_type)
  2609.     return (insertion_type_names[(int) type]);
  2610.   else
  2611.     return ("Broken-Type in insertion_type_pname");
  2612. }
  2613.  
  2614. /* Return the insertion_type associated with NAME.
  2615.    If the type is not one of the known ones, return BAD_TYPE. */
  2616. enum insertion_type
  2617. find_type_from_name (name)
  2618.      char *name;
  2619. {
  2620.   int index = 0;
  2621.   while (index < (int) bad_type)
  2622.     {
  2623.       if (stricmp (name, insertion_type_names[index]) == 0)
  2624.     return (enum insertion_type) index;
  2625.       index++;
  2626.     }
  2627.   return (bad_type);
  2628. }
  2629.  
  2630. void
  2631. do_nothing ()
  2632. {
  2633. }
  2634.  
  2635. int
  2636. defun_insertion (type)
  2637.      enum insertion_type type;
  2638. {
  2639.   return (type == defun ||
  2640.       type == defvar ||
  2641.       type == defopt ||
  2642.       type == deffn ||
  2643.       type == defspec ||
  2644.       type == defmac);
  2645. }
  2646.  
  2647. /* Non-zero means that we are currently hacking the insides of an
  2648.    insertion which would use a fixed width font. */
  2649. int in_fixed_width_font = 0;
  2650.  
  2651. /* This is where the work for all the "insertion" style
  2652.    commands is done.  A huge switch statement handles the
  2653.    various setups, and generic code is on both sides. */
  2654. void
  2655. begin_insertion (type)
  2656.      enum insertion_type type;
  2657. {
  2658.   int no_discard = 0;
  2659.  
  2660.   close_paragraph ();
  2661.  
  2662.   if (defun_insertion (type))
  2663.     {
  2664.       push_insertion (type, savestring (""));
  2665.       no_discard = 1;
  2666.     }
  2667.   else
  2668.     push_insertion (type, get_item_function ());
  2669.  
  2670.   filling_enabled = false;    /* the general case for insertions. */
  2671.   inhibit_paragraph_indentation = 1;
  2672.   no_indent = false;
  2673.  
  2674.   switch (type)
  2675.     {
  2676.     case menu:
  2677.       add_word ("* Menu:\n");
  2678.       in_menu++;
  2679.       discard_until ("\n");
  2680.       input_text_offset--;
  2681.       /* discard_until () has already counted the newline.  Discount it. */
  2682.       line_number--;
  2683.       return;
  2684.  
  2685.       /* I think @quotation is meant to do filling.
  2686.      If you don't want filling, then use @example. */
  2687.     case quotation:
  2688.       last_char_was_newline = 0;
  2689.       indented_fill = filling_enabled = true;
  2690.       current_indent += default_indentation_increment;
  2691.       break;
  2692.  
  2693.       /* Just like @example, but no indentation. */
  2694.     case format:
  2695.       in_fixed_width_font++;
  2696.       break;
  2697.  
  2698.     case display:
  2699.     case example:
  2700.     case smallexample:
  2701.     case lisp:
  2702.       last_char_was_newline = 0;
  2703.       current_indent += default_indentation_increment;
  2704.       in_fixed_width_font++;
  2705.       break;
  2706.  
  2707.     case table:
  2708.     case itemize:
  2709.       current_indent += default_indentation_increment;
  2710.       filling_enabled = indented_fill = true;
  2711.  
  2712.       /* Make things work for losers who forget the itemize syntax. */
  2713.       if (type == itemize)
  2714.     {
  2715.       if (!(*insertion_stack->item_function))
  2716.         {
  2717.           free (insertion_stack->item_function);
  2718.           insertion_stack->item_function = savestring ("*");
  2719.         }
  2720.     }
  2721.       break;
  2722.  
  2723.     case enumerate:
  2724.       inhibit_paragraph_indentation = 0;
  2725.       current_indent += default_indentation_increment;
  2726.       start_numbering (1);
  2727.       filling_enabled = indented_fill = true;
  2728.       break;
  2729.  
  2730.     case group:
  2731.       inhibit_paragraph_indentation = 0;
  2732.       break;
  2733.  
  2734.     case ifinfo:
  2735.       /* Undo whatever we just did.  This is a no-op. */
  2736.       inhibit_paragraph_indentation = 0;
  2737.       filling_enabled = insertion_stack->filling_enabled;
  2738.       indented_fill = insertion_stack->indented_fill;
  2739.       break;
  2740.  
  2741.     case defun:
  2742.     case defvar:
  2743.     case defopt:
  2744.     case deffn:
  2745.     case defspec:
  2746.     case defmac:
  2747.       filling_enabled = indented_fill = true;
  2748.       current_indent += default_indentation_increment;
  2749.       break;
  2750.     case bad_type:
  2751.       break;
  2752.     }
  2753.   if (!no_discard)
  2754.     discard_until ("\n");
  2755. }
  2756.  
  2757. /* Try to end the quotation with the specified type.
  2758.    Like begin_insertion (), this uses a giant switch statement as
  2759.    well.  A big difference is that you *must* pass a valid type to
  2760.    this function, and a value of bad_type gets translated to match
  2761.    the value currently on top of the stack.  If, however, the value
  2762.    passed is a valid type, and it doesn't match the top of the
  2763.    stack, then we produce an error.  Should fix this, somewhat
  2764.    unclean. */
  2765. void
  2766. end_insertion (type)
  2767.      enum insertion_type type;
  2768. {
  2769.   enum insertion_type temp_type;
  2770.  
  2771.   if (!insertion_level)
  2772.     return;
  2773.  
  2774.   temp_type = current_insertion_type ();
  2775.   if (type == bad_type)
  2776.     type = temp_type;
  2777.  
  2778.   if (type != temp_type)
  2779.     {
  2780.       line_error ("Expected `%s', but saw `%s'.  Token unread",
  2781.          insertion_type_pname (temp_type), insertion_type_pname (type));
  2782.       return;
  2783.     }
  2784.   pop_insertion ();
  2785.  
  2786.   switch (type)
  2787.     {
  2788.  
  2789.     case menu:
  2790.       in_menu--;        /* no longer hacking menus. */
  2791.       break;
  2792.  
  2793.     case enumerate:
  2794.       stop_numbering ();
  2795.       current_indent -= default_indentation_increment;
  2796.       break;
  2797.  
  2798.     case group:
  2799.     case ifinfo:
  2800.       break;
  2801.  
  2802.     case format:
  2803.     case example:
  2804.     case smallexample:
  2805.     case display:
  2806.     case lisp:
  2807.       in_fixed_width_font--;
  2808.       current_indent -= default_indentation_increment;
  2809.       break;
  2810.  
  2811.     default:
  2812.       current_indent -= default_indentation_increment;
  2813.       break;
  2814.     }
  2815.   close_paragraph ();
  2816. }
  2817.  
  2818. /* Insertions cannot cross certain boundaries, such as node beginnings.  In
  2819.    code that creates such boundaries, you should call discard_insertions ()
  2820.    before doing anything else.  It prints the errors for you, and cleans up
  2821.    the insertion stack. */
  2822. void
  2823. discard_insertions ()
  2824. {
  2825.   int real_line_number = line_number;
  2826.   while (insertion_stack)
  2827.     {
  2828.       if (insertion_stack->insertion == ifinfo)
  2829.     break;
  2830.       else
  2831.     {
  2832.       char *offender =
  2833.           (char *) insertion_type_pname (insertion_stack->insertion);
  2834.  
  2835.       line_number = insertion_stack->line_number;
  2836.       line_error ("This `%s' doesn't have a matching `%cend %s'", offender,
  2837.               COMMAND_PREFIX, offender);
  2838.       pop_insertion ();
  2839.     }
  2840.     }
  2841.   line_number = real_line_number;
  2842. }
  2843.  
  2844. /* MAX_NS is the maximum nesting level for enumerations.  I picked 100
  2845.    which seemed reasonable.  This doesn't control the number of items,
  2846.    just the number of nested lists. */
  2847. #define max_ns 100
  2848. int number_stack[max_ns];
  2849. int number_offset = 0;
  2850. int the_current_number = 0;
  2851.  
  2852. void
  2853. start_numbering (at_number)
  2854. int at_number;
  2855. {
  2856.   if (number_offset + 1 == max_ns)
  2857.     {
  2858.       line_error ("Enumeration stack overflow");
  2859.       return;
  2860.     }
  2861.   number_stack[number_offset++] = the_current_number;
  2862.   the_current_number = at_number;
  2863. }
  2864.  
  2865. void
  2866. stop_numbering ()
  2867. {
  2868.   the_current_number = number_stack[--number_offset];
  2869.   if (number_offset < 0)
  2870.     number_offset = 0;
  2871. }
  2872.  
  2873.  /* Place a number into the output stream. */
  2874. void
  2875. number_item ()
  2876. {
  2877.   char temp[10];
  2878.   sprintf (temp, "%d. ", the_current_number);
  2879.   indent (output_column += (current_indent - strlen (temp)));
  2880.   add_word (temp);
  2881.   the_current_number++;
  2882. }
  2883.  
  2884. /* The actual commands themselves. */
  2885.  
  2886. /* Commands which insert themselves. */
  2887. void
  2888. insert_self ()
  2889. {
  2890.   add_word (command);
  2891. }
  2892.  
  2893. /* Force line break */
  2894. void
  2895. cm_asterisk ()
  2896. {
  2897.   /* Force a line break in the output. */
  2898.   insert ('\n');
  2899.   indent (output_column = current_indent);
  2900. }
  2901.  
  2902. /* Insert ellipsis. */
  2903. void
  2904. cm_dots (arg)
  2905.      int arg;
  2906. {
  2907.   if (arg == START)
  2908.     add_word ("...");
  2909. }
  2910.  
  2911. void
  2912. cm_bullet (arg)
  2913.      int arg;
  2914. {
  2915.   if (arg == START)
  2916.     add_char ('*');
  2917. }
  2918.  
  2919. void
  2920. cm_minus (arg)
  2921.      int arg;
  2922. {
  2923.   if (arg == START)
  2924.     add_char ('-');
  2925. }
  2926.  
  2927. /* Insert "TeX". */
  2928. void
  2929. cm_TeX (arg)
  2930.      int arg;
  2931. {
  2932.   if (arg == START)
  2933.     add_word ("TeX");
  2934. }
  2935.  
  2936. void
  2937. cm_copyright (arg)
  2938.      int arg;
  2939. {
  2940.   if (arg == START)
  2941.     add_word ("(C)");
  2942. }
  2943.  
  2944. void
  2945. cm_code (arg)
  2946.      int arg;
  2947. {
  2948.   extern int printing_index;
  2949.  
  2950.   if (printing_index)
  2951.     return;
  2952.  
  2953.   add_char(arg == START ? '`' : '\'');
  2954. }
  2955.  
  2956. /***
  2957. void
  2958. cm_samp (arg)
  2959.      int arg;
  2960. {
  2961.   cm_code (arg);
  2962. }
  2963.  
  2964. void
  2965. cm_file (arg)
  2966.      int arg;
  2967. {
  2968.   cm_code (arg);
  2969. }
  2970.  
  2971. void
  2972. cm_kbd (arg)
  2973.      int arg;
  2974. {
  2975.   cm_code (arg);
  2976. }
  2977.  
  2978. void
  2979. cm_key (arg)
  2980.      int arg;
  2981. {
  2982. }
  2983. **/
  2984. void
  2985. cm_null (arg)
  2986.      int arg;
  2987. {
  2988. }
  2989.  
  2990.  /* Convert the character at position-1 into CTL. */
  2991. void
  2992. cm_ctrl (arg, position)
  2993.      int arg;
  2994.      size_t position;
  2995. {
  2996.   if (arg == END)
  2997.     output_paragraph[position - 1] = CTL (output_paragraph[position - 1]);
  2998. }
  2999.  
  3000. /* Small Caps in makeinfo just does all caps. */
  3001. void
  3002. cm_sc (arg, start_pos, end_pos)
  3003.      int arg;
  3004.      size_t start_pos, end_pos;
  3005. {
  3006.   if (arg == END)
  3007.     {
  3008.       while (start_pos < end_pos)
  3009.     {
  3010.       output_paragraph[start_pos] =
  3011.         coerce_to_upper (output_paragraph[start_pos]);
  3012.       start_pos++;
  3013.     }
  3014.     }
  3015. }
  3016.  
  3017. /* @var in makeinfo just uppercases the text. */
  3018. void
  3019. cm_var (arg, start_pos, end_pos)
  3020.      int arg;
  3021.      size_t start_pos, end_pos;
  3022. {
  3023.   if (arg == END)
  3024.     {
  3025.       while (start_pos < end_pos)
  3026.     {
  3027.       output_paragraph[start_pos] =
  3028.         coerce_to_upper (output_paragraph[start_pos]);
  3029.       start_pos++;
  3030.     }
  3031.     }
  3032. }
  3033.  
  3034. void
  3035. cm_dfn (arg, position)
  3036.      int arg;
  3037.      size_t position;
  3038. {
  3039.   add_char ('"');
  3040. }
  3041.  
  3042. void
  3043. cm_emph (arg)
  3044.      int arg;
  3045. {
  3046.   add_char ('*');
  3047. }
  3048.  
  3049. void
  3050. cm_strong (arg, position)
  3051.      int arg;
  3052.      size_t position;
  3053. {
  3054.   cm_emph (arg);
  3055. }
  3056.  
  3057. void
  3058. cm_cite (arg, position)
  3059.      int arg;
  3060.      size_t position;
  3061. {
  3062.   if (arg == START)
  3063.     add_word ("``");
  3064.   else
  3065.     add_word ("''");
  3066. }
  3067.  
  3068. /**
  3069. void
  3070. cm_italic (arg)
  3071. int arg;
  3072. {
  3073. }
  3074.  
  3075. void
  3076. cm_bold (arg)
  3077. int arg;
  3078. {
  3079.   cm_italic (arg);
  3080. }
  3081.  
  3082. void
  3083. cm_roman (arg)
  3084. int arg;
  3085. {
  3086. }
  3087.  
  3088. void
  3089. cm_title (arg)
  3090. int arg;
  3091. {
  3092.   cm_italic (arg);
  3093. }
  3094.  
  3095. void
  3096. cm_refill (arg)
  3097. int arg;
  3098. {
  3099. }
  3100. **/
  3101.  
  3102. /* Prevent the argument from being split across two lines. */
  3103. void
  3104. cm_w (arg)
  3105.      int arg;
  3106. {
  3107.   if (arg == START)
  3108.     non_splitting_words++;
  3109.   else
  3110.     non_splitting_words--;
  3111. }
  3112.  
  3113.  
  3114. /* Explain that this command is obsolete, thus the user shouldn't
  3115.    do anything with it. */
  3116. void
  3117. cm_obsolete (arg)
  3118.    int arg;
  3119. {
  3120.   if (arg == START)
  3121.     warning ("The command `@%s' is obsolete", command);
  3122. }
  3123.  
  3124. /* Insert the text following input_text_offset up to the end of the line
  3125.    in a new, separate paragraph.  Directly underneath it, insert a
  3126.    line of WITH_CHAR, the same length of the inserted text. */
  3127. void
  3128. insert_and_underscore (with_char)
  3129.      int with_char;
  3130. {
  3131.   size_t len, i;
  3132.   int old_no_indent;
  3133.   char *temp;
  3134.  
  3135.   close_paragraph ();
  3136.   filling_enabled =  indented_fill = false;
  3137.   old_no_indent = no_indent;
  3138.   no_indent = true;
  3139.   get_rest_of_line (&temp);
  3140.  
  3141.   len = output_position;
  3142.   execute_string ("%s\n", temp);
  3143.   free (temp);
  3144.  
  3145.   len = ((output_position + output_paragraph_offset) - 1) - len;
  3146.   for (i = 0; i < len; i++)
  3147.     add_char (with_char);
  3148.   insert ('\n');
  3149.   close_paragraph ();
  3150.   filling_enabled = true;
  3151.   no_indent = old_no_indent;
  3152. }
  3153.  
  3154. #ifndef atarist
  3155. void
  3156. cm_chapter ()
  3157. {
  3158.   insert_and_underscore ('*');
  3159. }
  3160.  
  3161. void
  3162. cm_section ()
  3163. {
  3164.   insert_and_underscore ('=');
  3165. }
  3166.  
  3167. void
  3168. cm_subsection ()
  3169. {
  3170.   insert_and_underscore ('-');
  3171. }
  3172.  
  3173. void
  3174. cm_subsubsection ()
  3175. {
  3176.   insert_and_underscore ('.');
  3177. }
  3178.  
  3179. void
  3180. cm_unnumbered ()
  3181. {
  3182.   cm_chapter ();
  3183. }
  3184.  
  3185. void
  3186. cm_unnumberedsec ()
  3187. {
  3188.   cm_section ();
  3189. }
  3190.  
  3191. void
  3192. cm_unnumberedsubsec ()
  3193. {
  3194.   cm_subsection ();
  3195. }
  3196.  
  3197. void
  3198. cm_unnumberedsubsubsec ()
  3199. {
  3200.   cm_subsubsection ();
  3201. }
  3202.  
  3203. void
  3204. cm_appendix ()
  3205. {
  3206.   cm_chapter ();
  3207. }
  3208.  
  3209. void
  3210. cm_appendixsec ()
  3211. {
  3212.   cm_section ();
  3213. }
  3214.  
  3215. void
  3216. cm_appendixsubsec ()
  3217. {
  3218.   cm_subsection ();
  3219. }
  3220.  
  3221. void
  3222. cm_appendixsubsubsec ()
  3223. {
  3224.   cm_subsubsection ();
  3225. }
  3226.  
  3227. void
  3228. cm_majorheading ()
  3229. {
  3230.   cm_chapheading ();
  3231. }
  3232.  
  3233. void
  3234. cm_chapheading ()
  3235. {
  3236.   cm_chapter ();
  3237. }
  3238.  
  3239. void
  3240. cm_heading ()
  3241. {
  3242.   cm_section ();
  3243. }
  3244.  
  3245. void
  3246. cm_subheading ()
  3247. {
  3248.   cm_subsection ();
  3249. }
  3250.  
  3251. void
  3252. cm_subsubheading ()
  3253. {
  3254.   cm_subsubsection ();
  3255. }
  3256. #else /* atarist */
  3257. asm(".stabs \"_cm_unnumbered\",5,0,0,_cm_chapter");
  3258. asm(".stabs \"_cm_appendix\",5,0,0,_cm_chapter");
  3259. asm(".stabs \"_cm_majorheading\",5,0,0,_cm_chapter");
  3260. asm(".stabs \"_cm_chapheading\",5,0,0,_cm_chapter");
  3261. void
  3262. cm_chapter ()
  3263. {
  3264.   insert_and_underscore ('*');
  3265. }
  3266.  
  3267. asm(".stabs \"_cm_unnumberedsec\",5,0,0,_cm_section");
  3268. asm(".stabs \"_cm_appendixsec\",5,0,0,_cm_section");
  3269. asm(".stabs \"_cm_heading\",5,0,0,_cm_section");
  3270. void
  3271. cm_section ()
  3272. {
  3273.   insert_and_underscore ('=');
  3274. }
  3275.  
  3276. asm(".stabs \"_cm_unnumberedsubsec\",5,0,0,_cm_subsection");
  3277. asm(".stabs \"_cm_appendixsubsec\",5,0,0,_cm_subsection");
  3278. asm(".stabs \"_cm_subheading\",5,0,0,_cm_subsection");
  3279. void
  3280. cm_subsection ()
  3281. {
  3282.   insert_and_underscore ('-');
  3283. }
  3284.  
  3285. asm(".stabs \"_cm_unnumberedsubsubsec\",5,0,0,_cm_subsubsection");
  3286. asm(".stabs \"_cm_appendixsubsubsec\",5,0,0,_cm_subsubsection");
  3287. asm(".stabs \"_cm_subsubheading\",5,0,0,_cm_subsubsection");
  3288. void
  3289. cm_subsubsection ()
  3290. {
  3291.   insert_and_underscore ('.');
  3292. }
  3293.  
  3294. #endif /* atarist */
  3295.  
  3296. /* **************************************************************** */
  3297. /*                                    */
  3298. /*           Adding nodes, and making tags            */
  3299. /*                                    */
  3300. /* **************************************************************** */
  3301.  
  3302. /* Start a new tag table. */
  3303. void
  3304. init_tag_table ()
  3305. {
  3306.   while (tag_table != (TAG_ENTRY *) NULL)
  3307.     {
  3308.       TAG_ENTRY *temp = tag_table;
  3309.       free (temp->node);
  3310.       free (temp->prev);
  3311.       free (temp->next);
  3312.       free (temp->up);
  3313.       tag_table = tag_table->next_ent;
  3314.       free (temp);
  3315.     }
  3316. }
  3317.  
  3318. void
  3319. write_tag_table ()
  3320. {
  3321.   return (write_tag_table_internal (false));    /* Not indirect. */
  3322. }
  3323.  
  3324. void
  3325. write_tag_table_indirect ()
  3326. {
  3327.   return (write_tag_table_internal (true));
  3328. }
  3329.  
  3330. /* Write out the contents of the existing tag table.
  3331.    INDIRECT_P says how to format the output. */
  3332. void
  3333. write_tag_table_internal (indirect_p)
  3334.      boolean indirect_p;
  3335. {
  3336.   TAG_ENTRY *node = tag_table;
  3337.  
  3338.   filling_enabled = false;
  3339.   must_start_paragraph = 0;
  3340.   close_paragraph ();
  3341.   if (!indirect_p)
  3342.     insert ('\n');
  3343.   add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
  3344.  
  3345.   while (node != (TAG_ENTRY *) NULL)
  3346.     {
  3347.       add_word_args ("Node: %s\177%ld\n", node->node, node->position);
  3348.       node = node->next_ent;
  3349.     }
  3350.   add_word ("\037\nEnd Tag Table\n");
  3351.   flush_output ();
  3352. }
  3353.  
  3354. char *
  3355. get_node_token ()
  3356. {
  3357.   char *string;
  3358.  
  3359.   get_until_in_line (",", &string);
  3360.  
  3361.   if (curchar () == ',')
  3362.     input_text_offset++;
  3363.  
  3364.   /* canon_white (string); */
  3365.  
  3366.   /* Allow things like @@nodename. */
  3367.   normalize_node_name (string);
  3368.  
  3369.   return (string);
  3370. }
  3371.  
  3372. /* Given a node name in STRING, remove double @ signs, replacing them
  3373.    with just one. */
  3374. void
  3375. normalize_node_name (string)
  3376.      char *string;
  3377. {
  3378.   register int i, l = strlen (string);
  3379.  
  3380.   for (i = 0; i < l; i++)
  3381.     {
  3382.       if (string[i] == '@' && string[i + 1] == '@')
  3383.     {
  3384.       strncpy (string + i, string + i + 1, l - i);
  3385.       l--;
  3386.     }
  3387.     }
  3388. }
  3389.  
  3390. /* Look up NAME in the tag table, and return the associated
  3391.    tag_entry.  If the node is not in the table return NULL. */
  3392. TAG_ENTRY *
  3393. find_node (name)
  3394.      char *name;
  3395. {
  3396.   TAG_ENTRY *tag = tag_table;
  3397.  
  3398.   while (tag != (TAG_ENTRY *) NULL)
  3399.     {
  3400.       if (stricmp (tag->node, name) == 0)
  3401.     return (tag);
  3402.       tag = tag->next_ent;
  3403.     }
  3404.   return ((TAG_ENTRY *) NULL);
  3405. }
  3406.  
  3407. /* Remember NODE and associates. */
  3408. void
  3409. remember_node (node, prev, next, up, position, line_no, no_warn)
  3410.      char *node, *prev, *next, *up;
  3411.      size_t position, line_no;
  3412.      int no_warn;
  3413. {
  3414.   /* Check for existence of this tag already. */
  3415.   if (validating)
  3416.     {
  3417.       register TAG_ENTRY *tag = find_node (node);
  3418.       if (tag)
  3419.     {
  3420.       line_error ("Node `%s' multiply defined (%ld is first definition)",
  3421.               node, tag->line_no);
  3422.       return;
  3423.     }
  3424.     }
  3425.  
  3426.   /* First, make this the current node. */
  3427.   current_node = node;
  3428.  
  3429.   /* Now add it to the list. */
  3430.   {
  3431.     TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY));
  3432.     new->node = node;
  3433.     new->prev = prev;
  3434.     new->next = next;
  3435.     new->up = up;
  3436.     new->position = position;
  3437.     new->line_no = line_no;
  3438.     new->filename = node_filename;
  3439.     new->touched = 0;        /* not yet referenced. */
  3440.     new->flags = 0;
  3441.     if (no_warn)
  3442.       new->flags |= NO_WARN;
  3443.     new->next_ent = tag_table;
  3444.     tag_table = new;
  3445.   }
  3446. }
  3447.  
  3448. /* Here is a structure which associates sectioning commands with
  3449.    an integer, hopefully to reflect the `depth' of the current
  3450.    section. */
  3451. struct {
  3452.   char *name;
  3453.   int level;
  3454. } section_alist[] = {
  3455.   { "chapter", 1 },
  3456.   { "section", 2},
  3457.   { "subsec", 3},
  3458.   { "subsubsec", 4},
  3459.   { "unnumbered", 1},
  3460.   { "unnumberedsec", 2},
  3461.   { "unnumberedsubsec", 3},
  3462.   { "unnumberedsubsubsec", 4},
  3463.   { "appendix", 1},
  3464.   { "appendixsec", 2},
  3465.   { "appendixsubsec", 3},
  3466.   { "appendixsubsubsec", 4},
  3467.   { (char *)NULL, 0 }
  3468. };
  3469.  
  3470. /* Return an integer which identifies the type section present in TEXT. */
  3471. int
  3472. what_section (text)
  3473.      char *text;
  3474. {
  3475.   int i, j;
  3476.   char *t;
  3477.  
  3478.   for (j = 0; text[j] && whitespace (text[j]) || text[j] == '\n'; j++);
  3479.   if (text[j] != '@')
  3480.     return (-1);
  3481.  
  3482.   text = text + j + 1;
  3483.  
  3484.   /* Handle italicized sectioning commands. */
  3485.   if (*text == 'i')
  3486.     text++;
  3487.  
  3488.   for (i = 0; t = section_alist[i].name; i++)
  3489.     {
  3490.       if (strncmp (t, text, strlen (t)) == 0)
  3491.     return (section_alist[i].level);
  3492.     }
  3493.   return (-1);
  3494. }
  3495.  
  3496. /* The order is: nodename, nextnode, prevnode, upnode.
  3497.    The next, prev, and up fields can be defaulted.
  3498.    You must follow a node command which has those fields
  3499.    defaulted with a sectioning command (e.g. @chapter) giving
  3500.    the "level" of that node.  It is an error not to do so.
  3501.    The defaults come from the menu in this nodes parent. */
  3502. void
  3503. cm_node ()
  3504. {
  3505.   char *node, *prev, *next, *up;
  3506.   size_t new_node_pos;
  3507.   int defaulting, this_section, no_warn = 0;
  3508.   extern int already_outputting_pending_notes;
  3509.  
  3510.   if (strcmp (command, "nwnode") == 0)
  3511.     no_warn = 1;
  3512.  
  3513.   /* Get rid of unmatched brace arguments from previous commands. */
  3514.   discard_braces ();
  3515.  
  3516.   /* There also might be insertions left lying around that haven't been
  3517.      ended yet.  Do that also. */
  3518.   discard_insertions ();
  3519.  
  3520.   if (!already_outputting_pending_notes)
  3521.     {
  3522.       close_paragraph ();
  3523.       output_pending_notes ();
  3524.       free_pending_notes ();
  3525.     }
  3526.  
  3527.   filling_enabled = indented_fill = false;
  3528.   new_node_pos = output_position + 1;
  3529.  
  3530.   node = get_node_token ();
  3531.   next = get_node_token ();
  3532.   prev = get_node_token ();
  3533.   up = get_node_token ();
  3534.  
  3535.   this_section = what_section (input_text + input_text_offset);
  3536.  
  3537.   /* ??? The first \n in the following string shouldn't be there, but I have
  3538.      to revamp the @example & @group things so that they always leave a \n
  3539.      as the last character output.  Until that time, this is the only way
  3540.      I can produce reliable output. */
  3541.   no_indent = true;
  3542.   add_word_args ("\n\037\nFile: %s,  Node: %s", pretty_output_filename, node);
  3543.  
  3544.   /* Check for defaulting of this node's next, prev, and up fields. */
  3545.   defaulting = ((strlen (next) == 0) &&
  3546.         (strlen (prev) == 0) &&
  3547.         (strlen (up) == 0));
  3548.  
  3549.   /* If we are defaulting, then look at the immediately following
  3550.      sectioning command (error if none) to determine the node's
  3551.      level.  Find the node that contains the menu mentioning this node
  3552.      that is one level up (error if not found).  That node is the "Up"
  3553.      of this node.  Default the "Next" and "Prev" from the menu. */
  3554.   if (defaulting)
  3555.     {
  3556.       NODE_REF *last_ref = (NODE_REF *)NULL;
  3557.       NODE_REF *ref = node_references;
  3558.  
  3559.       if (this_section < 0)
  3560.     {
  3561.       char *polite_section_name = "chapter";
  3562.       int i;
  3563.  
  3564.       for (i = 0; section_alist[i].name; i++)
  3565.         if (section_alist[i].level == current_section + 1)
  3566.           {
  3567.         polite_section_name = section_alist[i].name;
  3568.         break;
  3569.           }
  3570.  
  3571.       line_error
  3572.         ("Node `%s' requires a sectioning command (e.g. @%s)",
  3573.          node, polite_section_name);
  3574.     }
  3575.       else
  3576.     {
  3577.       while (ref)
  3578.         {
  3579.           if (ref->section == (this_section - 1) &&
  3580.           ref->type == menu_reference &&
  3581.           stricmp (ref->node, node) == 0)
  3582.         {
  3583.           free (up);
  3584.           up = savestring (ref->containing_node);
  3585.  
  3586.           if (last_ref &&
  3587.               strcmp
  3588.               (last_ref->containing_node, ref->containing_node) == 0)
  3589.             {
  3590.               free (next);
  3591.               next = savestring (last_ref->node);
  3592.             }
  3593.  
  3594.           if (ref->next &&
  3595.               strcmp
  3596.               (ref->next->containing_node, ref->containing_node) == 0)
  3597.             {
  3598.               free (prev);
  3599.               prev = savestring (ref->next->node);
  3600.             }
  3601.           break;
  3602.         }
  3603.           last_ref = ref;
  3604.           ref = ref->next;
  3605.         }
  3606.     }
  3607.     }
  3608.  
  3609.   if (*next)
  3610.     add_word_args (",  Next: %s", next);
  3611.   
  3612.   if (*prev)
  3613.     add_word_args (",  Prev: %s", prev);
  3614.  
  3615.   if (*up)
  3616.     add_word_args (",  Up: %s", up);
  3617.  
  3618.   insert ('\n');
  3619.   close_paragraph ();
  3620.   no_indent = false;
  3621.  
  3622.   if (!*node)
  3623.     {
  3624.       line_error ("No node name specified for `@%s' command", command);
  3625.       free (node);
  3626.       free (next);
  3627.       free (prev);
  3628.       free (up);
  3629.     }
  3630.   else
  3631.     {
  3632.       if (!*next) { free (next); next = (char *)NULL; }
  3633.       if (!*prev) { free (prev); prev = (char *)NULL; }
  3634.       if (!*up) { free (up); up = (char *)NULL; }
  3635.       remember_node (node, prev, next, up, new_node_pos, line_number, no_warn);
  3636.     }
  3637.  
  3638.   /* Change the section only if there was a sectioning command. */
  3639.   if (this_section >= 0)
  3640.     current_section = this_section;
  3641.  
  3642.   filling_enabled = true;
  3643. }
  3644.  
  3645. /* Validation of an info file.
  3646.    Scan through the list of tag entrys touching the Prev, Next, and Up
  3647.    elements of each.  It is an error not to be able to touch one of them,
  3648.    except in the case of external node references, such as "(DIR)".
  3649.  
  3650.    If the Prev is different from the Up,
  3651.    then the Prev node must have a Next pointing at this node.
  3652.  
  3653.    Every node except Top must have an Up.
  3654.    The Up node must contain some sort of reference, other than a Next,
  3655.    to this node.
  3656.  
  3657.    If the Next is different from the Next of the Up,
  3658.    then the Next node must have a Prev pointing at this node. */
  3659. void
  3660. validate_file (filename, tag_table)
  3661.      char *filename;
  3662.      TAG_ENTRY *tag_table;
  3663. {
  3664.   char *old_input_filename = input_filename;
  3665.   TAG_ENTRY *tags = tag_table;
  3666.  
  3667.   while (tags != (TAG_ENTRY *) NULL)
  3668.     {
  3669.       register TAG_ENTRY *temp_tag;
  3670.  
  3671.       input_filename = tags->filename;
  3672.       line_number = tags->line_no;
  3673.  
  3674.       /* If this node has a Next, then make sure that the Next exists. */
  3675.       if (tags->next)
  3676.     {
  3677.       validate (tags->next, tags->line_no, "Next");
  3678.  
  3679.       /* If the Next node exists, and there is no Up, then make
  3680.          sure that the Prev of the Next points back. */
  3681.       if (temp_tag = find_node (tags->next))
  3682.         {
  3683.           char *prev = temp_tag->prev;
  3684.           if (!prev || (stricmp (prev, tags->node) != 0))
  3685.         {
  3686.           line_error
  3687.             ("Node `%s''s Next field not pointed back to", tags->node);
  3688.           line_number = temp_tag->line_no;
  3689.           input_filename = temp_tag->filename;
  3690.           line_error
  3691.             ("This node (`%s') is the one with the bad `Prev'",
  3692.              temp_tag->node);
  3693.           input_filename = tags->filename;
  3694.           line_number = tags->line_no;
  3695.           temp_tag->flags |= PREV_ERROR;
  3696.         }
  3697.         }
  3698.     }
  3699.  
  3700.       /* Validate the Prev field if there is one, and we haven't already
  3701.      complained about it in some way.  You don't have to have a Prev
  3702.      field at this stage. */
  3703.       if (!(tags->flags & PREV_ERROR) && tags->prev)
  3704.     {
  3705.       int valid = validate (tags->prev, tags->line_no, "Prev");
  3706.  
  3707.       if (!valid)
  3708.         tags->flags |= PREV_ERROR;
  3709.       else
  3710.         {
  3711.           /* If the Prev field is not the same as the Up field,
  3712.          then the node pointed to by the Prev field must have
  3713.          a Next field which points to this node. */
  3714.           if (tags->up && (stricmp (tags->prev, tags->up) != 0))
  3715.         {
  3716.           temp_tag = find_node (tags->prev);
  3717.           if (!temp_tag->next ||
  3718.               (stricmp (temp_tag->next, tags->node) != 0))
  3719.             {
  3720.               line_error ("Node `%s''s Prev field not pointed back to",
  3721.                   tags->node);
  3722.               line_number = temp_tag->line_no;
  3723.               input_filename = temp_tag->filename;
  3724.               line_error
  3725.             ("This node (`%s') is the one with the bad `Next'",
  3726.              temp_tag->node);
  3727.               input_filename = tags->filename;
  3728.               line_number = tags->line_no;
  3729.               temp_tag->flags |= NEXT_ERROR;
  3730.             }
  3731.         }
  3732.         }
  3733.     }
  3734.  
  3735.       if (!tags->up && (stricmp (tags->node, "Top") != 0))
  3736.     line_error ("Node `%s' is missing an \"Up\" field", tags->node);
  3737.       else if (tags->up)
  3738.     {
  3739.       int valid = validate (tags->up, tags->line_no, "Up");
  3740.  
  3741.       /* If node X has Up: Y, then warn if Y fails to have a menu item
  3742.          or note pointing at X, if Y isn't of the form "(Y)". */
  3743.       if (valid && *tags->up != '(')
  3744.         {
  3745.           NODE_REF *nref, *tref, *list;
  3746. /*          NODE_REF *find_node_reference ();  */
  3747.  
  3748.           tref = (NODE_REF *) NULL;
  3749.           list = node_references;
  3750.  
  3751.           for (;;)
  3752.         {
  3753.           if (!(nref = find_node_reference (tags->node, list)))
  3754.             break;
  3755.  
  3756.           if (stricmp (nref->containing_node, tags->up) == 0)
  3757.             {
  3758.               if (nref->type != menu_reference)
  3759.             {
  3760.               tref = nref;
  3761.               list = nref->next;
  3762.             }
  3763.               else
  3764.             break;
  3765.             }
  3766.           list = nref->next;
  3767.         }
  3768.  
  3769.           if (!nref)
  3770.         {
  3771.           temp_tag = find_node (tags->up);
  3772.           line_number = temp_tag->line_no;
  3773.           filename = temp_tag->filename;
  3774.           if (!tref)
  3775.             line_error (
  3776.         "`%s' has an Up field of `%s', but `%s' has no menu item for `%s'",
  3777.                 tags->node, tags->up, tags->up, tags->node);
  3778.           line_number = tags->line_no;
  3779.           filename = tags->filename;
  3780.         }
  3781.         }
  3782.     }
  3783.       tags = tags->next_ent;
  3784.     }
  3785.  
  3786.   validate_other_references (node_references);
  3787.   /* We have told the user about the references which didn't exist.
  3788.      Now tell him about the nodes which aren't referenced. */
  3789.  
  3790.   tags = tag_table;
  3791.   while (tags != (TAG_ENTRY *) NULL)
  3792.     {
  3793.       /* Special hack.  If the node in question appears to have
  3794.          been referenced more than REFERENCE_WARNING_LIMIT times,
  3795.          give a warning. */
  3796.       if (tags->touched > reference_warning_limit)
  3797.     {
  3798.       input_filename = tags->filename;
  3799.       line_number = tags->line_no;
  3800.       warning ("Node `%s' has been referenced %d times",
  3801.            tags->node, tags->touched);
  3802.     }
  3803.  
  3804.       if (tags->touched == 0)
  3805.     {
  3806.       input_filename = tags->filename;
  3807.       line_number = tags->line_no;
  3808.  
  3809.       /* Notice that the node "Top" is special, and doesn't have to
  3810.          be referenced. */
  3811.       if (stricmp (tags->node, "Top") != 0)
  3812.         warning ("Unreferenced node `%s'", tags->node);
  3813.     }
  3814.       tags = tags->next_ent;
  3815.     }
  3816.   input_filename = old_input_filename;
  3817. }
  3818.  
  3819. /* Return 1 if tag correctly validated, or 0 if not. */
  3820. int
  3821. validate (tag, line, label)
  3822.      char *tag;
  3823.      size_t line;
  3824.      char *label;
  3825. {
  3826.   TAG_ENTRY *result;
  3827.  
  3828.   /* If there isn't a tag to verify, or if the tag is in another file,
  3829.      then it must be okay. */
  3830.   if (!tag || !*tag || *tag == '(')
  3831.     return (1);
  3832.  
  3833.   /* Otherwise, the tag must exist. */
  3834.   result = find_node (tag);
  3835.  
  3836.   if (!result)
  3837.     {
  3838.       line_number = line;
  3839.       line_error (
  3840.      "Validation error.  `%s' field points to node `%s', which doesn't exist",
  3841.           label, tag);
  3842.       return (0);
  3843.     }
  3844.   result->touched++;
  3845.   return (1);
  3846. }
  3847.  
  3848. /* Split large output files into a series of smaller files.  Each file
  3849.    is pointed to in the tag table, which then gets written out as the
  3850.    original file.  The new files have the same name as the original file
  3851.    with a "-num" attached.  SIZE is the largest number of bytes to allow
  3852.    in any single split file. */
  3853. /* For MessyDOS like file system we will usurp the whole extension for
  3854.    file number, in a form of .-num so we may have up to 99 files */
  3855. void
  3856. split_file (filename, size)
  3857.      char *filename;
  3858.      size_t size;
  3859. {
  3860.   char *root_filename, *root_pathname;
  3861.   char *the_file;
  3862.   struct stat fileinfo;
  3863.   char *the_header;
  3864.   int header_size;
  3865.  
  3866.   /* Can only do this to files with tag tables. */
  3867.   if (!tag_table)
  3868.     return;
  3869.  
  3870.   if (size == 0)
  3871.     size = DEFAULT_SPLIT_SIZE;
  3872.  
  3873.   if ((stat (filename, &fileinfo) != 0) ||
  3874.       (fileinfo.st_size < SPLIT_SIZE_THRESHOLD))
  3875.     return;
  3876.  
  3877.   the_file = find_and_load (filename);
  3878.   if (!the_file)
  3879.     return;
  3880.  
  3881.   root_filename = filename_part (filename);
  3882. #ifdef atarist
  3883.   {
  3884.       char *loc;
  3885.       if (NULL != (loc = rindex (root_filename, '.')))
  3886.       *(loc + 1) = '\0';
  3887.   }
  3888. #endif
  3889.   root_pathname = pathname_part (filename);
  3890.  
  3891.   if (!root_pathname)
  3892.     root_pathname = savestring ("");
  3893.  
  3894.   /* Start splitting the file.  Walk along the tag table
  3895.      outputting sections of the file.  When we have written
  3896.      all of the nodes in the tag table, make the top-level
  3897.      pointer file, which contains indirect pointers and
  3898.      tags for the nodes. */
  3899.   {
  3900.     int which_file = 1;
  3901.     TAG_ENTRY *tags = tag_table;
  3902.     char *indirect_info = (char *)NULL;
  3903.  
  3904.     /* Remember the `header' of this file.  The first tag in the file is
  3905.        the bottom of the header; the top of the file is the start. */
  3906.     the_header = xmalloc (1 + (header_size = (tags->position - 2)));
  3907.     bcopy (the_file, the_header, header_size);
  3908.  
  3909.     while (tags)
  3910.       {
  3911.     size_t file_top, file_bot, limit;
  3912.  
  3913.     /* Have to include the Control-_. */
  3914.     file_top = file_bot = tags->position - 2;
  3915.     limit = file_top + size;
  3916.  
  3917.     /* If the rest of this file is only one node, then
  3918.        that is the entire subfile. */
  3919.     if (!tags->next_ent)
  3920.       {
  3921.         size_t i = tags->position + 1;
  3922.         char last_char = the_file[i];
  3923.  
  3924.         while (i < fileinfo.st_size)
  3925.           {
  3926.         if ((the_file[i] == '\037') &&
  3927.             ((last_char == '\n') ||
  3928.              (last_char == '\014')))
  3929.           break;
  3930.         else
  3931.           last_char = the_file[i];
  3932.         i++;
  3933.           }
  3934.         file_bot = i;
  3935.         tags = tags->next_ent;
  3936.         goto write_region;
  3937.       }
  3938.  
  3939.     /* Otherwise, find the largest number of nodes that can fit in
  3940.        this subfile. */
  3941.     for (; tags; tags = tags->next_ent)
  3942.       {
  3943.         if (!tags->next_ent)
  3944.           {
  3945.         /* This entry is the last node.  Search forward for the end
  3946.                of this node, and that is the end of this file. */
  3947.         size_t i = tags->position + 1;
  3948.         char last_char = the_file[i];
  3949.  
  3950.         while (i < fileinfo.st_size)
  3951.           {
  3952.             if ((the_file[i] == '\037') &&
  3953.             ((last_char == '\n') ||
  3954.              (last_char == '\014')))
  3955.               break;
  3956.             else
  3957.               last_char = the_file[i];
  3958.             i++;
  3959.           }
  3960.         file_bot = i;
  3961.  
  3962.         if (file_bot < limit)
  3963.           {
  3964.             tags = tags->next_ent;
  3965.             goto write_region;
  3966.           }
  3967.         else
  3968.           {
  3969.             /* Here we want to write out everything before the last
  3970.                node, and then write the last node out in a file
  3971.                by itself. */
  3972.             file_bot = tags->position;
  3973.             goto write_region;
  3974.           }
  3975.           }
  3976.  
  3977.         if (tags->next_ent->position > limit)
  3978.           {
  3979.         if ((tags->position) - 2 == file_top)
  3980.           tags = tags->next_ent;
  3981.         file_bot = tags->position;
  3982.           write_region:
  3983.         {
  3984.           int fd;
  3985.           char *split_file = xmalloc (10 + strlen (root_pathname)
  3986.                           + strlen (root_filename));
  3987.           sprintf (split_file,
  3988.                "%s%s-%d", root_pathname, root_filename, which_file);
  3989. #ifdef atarist
  3990. #define write _write
  3991. #endif
  3992.  
  3993.           if (((fd =
  3994.             open (split_file, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
  3995.               || (write (fd, the_header, header_size) != header_size)
  3996.               || (write (fd, the_file + file_top, file_bot - file_top)
  3997.               != (file_bot - file_top))
  3998.               || ((close (fd)) < 0))
  3999.             {
  4000.               perror (split_file);
  4001.               close (fd);
  4002.               exit (FATAL);
  4003.             }
  4004. #ifdef atarist
  4005. #undef write
  4006. #endif
  4007.  
  4008.           if (!indirect_info)
  4009.             {
  4010.               indirect_info = the_file + file_top;
  4011.               sprintf (indirect_info, "\037\nIndirect:\n");
  4012.               indirect_info += strlen (indirect_info);
  4013.             }
  4014.  
  4015.           sprintf (indirect_info, "%s-%d: %ld\n",
  4016.                root_filename, which_file, file_top);
  4017.  
  4018.           free (split_file);
  4019.           indirect_info += strlen (indirect_info);
  4020.           which_file++;
  4021.           break;
  4022.         }
  4023.           }
  4024.       }
  4025.       }
  4026.  
  4027.     /* We have sucessfully created the subfiles.  Now write out the
  4028.        original again.  We must use `output_stream', or
  4029.        write_tag_table_indirect () won't know where to place the output. */
  4030.     output_stream = fopen (filename, "w");
  4031.     if (!output_stream)
  4032.       {
  4033.     perror (filename);
  4034.     exit (FATAL);
  4035.       }
  4036.  
  4037.     {
  4038.       size_t distance = indirect_info - the_file;
  4039.       fwrite (the_file, 1, distance, output_stream);
  4040.  
  4041.       /* Inhibit newlines. */
  4042.       paragraph_is_open = false;
  4043.  
  4044.       write_tag_table_indirect ();
  4045.       fclose (output_stream);
  4046.       free (the_header);
  4047.       free (the_file);
  4048.       return;
  4049.     }
  4050.   }
  4051. }
  4052.  
  4053. /* Some menu hacking.  This is used to remember menu references while
  4054.    reading the input file.  After the output file has been written, if
  4055.    validation is on, then we use the contents of NODE_REFERENCES as a
  4056.    list of nodes to validate. */
  4057. char *
  4058. reftype_type_string (type)
  4059.      enum reftype type;
  4060. {
  4061.   switch (type)
  4062.     {
  4063.     case menu_reference:
  4064.       return ("Menu");
  4065.     case followed_reference:
  4066.       return ("Followed-Reference");
  4067.     default:
  4068.       return ("Internal-bad-reference-type");
  4069.     }
  4070. }
  4071.  
  4072. /* Remember this node name for later validation use. */
  4073. void
  4074. remember_node_reference (node, line, type)
  4075.      char *node;
  4076.      int line;
  4077.      enum reftype type;
  4078. {
  4079.   NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF));
  4080.  
  4081.   temp->next = node_references;
  4082.   temp->node = savestring (node);
  4083.   temp->line_no = line;
  4084.   temp->section = current_section;
  4085.   temp->type = type;
  4086.   temp->containing_node = savestring (current_node);
  4087.   temp->filename = node_filename;
  4088.  
  4089.   node_references = temp;
  4090. }
  4091.  
  4092. void
  4093. validate_other_references (ref_list)
  4094.      register NODE_REF *ref_list;
  4095. {
  4096.   char *old_input_filename = input_filename;
  4097.  
  4098.   while (ref_list != (NODE_REF *) NULL)
  4099.     {
  4100.       input_filename = ref_list->filename;
  4101.       validate (ref_list->node, ref_list->line_no,
  4102.         reftype_type_string (ref_list->type));
  4103.       ref_list = ref_list->next;
  4104.     }
  4105.   input_filename = old_input_filename;
  4106. }
  4107.  
  4108. /* Find NODE in REF_LIST. */
  4109. NODE_REF *
  4110. find_node_reference (node, ref_list)
  4111.      char *node;
  4112.      register NODE_REF *ref_list;
  4113. {
  4114.   while (ref_list)
  4115.     {
  4116.       if (stricmp (node, ref_list->node) == 0)
  4117.     break;
  4118.       ref_list = ref_list->next;
  4119.     }
  4120.   return (ref_list);
  4121. }
  4122.  
  4123. void
  4124. free_node_references ()
  4125. {
  4126.   register NODE_REF *list, *temp;
  4127.  
  4128.   list = node_references;
  4129.  
  4130.   while (list)
  4131.     {
  4132.       temp = list;
  4133.       free (list->node);
  4134.       free (list->containing_node);
  4135.       list = list->next;
  4136.       free (temp);
  4137.     }
  4138.   node_references = (NODE_REF *) NULL;
  4139. }
  4140.  
  4141. #define menu_starter "* "
  4142. void
  4143. glean_node_from_menu ()
  4144. {
  4145.   /* This function gets called at the start of every line while inside of
  4146.      a menu.  It checks to see if the line starts with "* ", and if so,
  4147.      remembers the node reference that this menu refers to.
  4148.  
  4149.      input_text_offset is at the \n just before the line start. */
  4150.  
  4151.   size_t i, orig_offset = input_text_offset;
  4152.   char *nodename;
  4153.  
  4154.   if (strncmp (&input_text[input_text_offset + 1],
  4155.            menu_starter,
  4156.            strlen (menu_starter)) != 0)
  4157.     return;
  4158.   else
  4159.     input_text_offset += strlen (menu_starter) + 1;
  4160.  
  4161.   get_until_in_line (":", &nodename);
  4162.   if (curchar () == ':')
  4163.     input_text_offset++;
  4164.   /* canon_white (nodename); */
  4165.  
  4166.   if (curchar () == ':')
  4167.     goto save_node;
  4168.   free (nodename);
  4169.   get_rest_of_line (&nodename);
  4170.  
  4171.   /* Special hack: If the nodename follows the menu item name,
  4172.      then we have to read the rest of the line in order to find
  4173.      out what the nodename is.  But we still have to read the
  4174.      line later, in order to process any formatting commands that
  4175.      might be present.  So un-count the carriage return that has just
  4176.      been counted. */
  4177.   line_number--;
  4178.  
  4179.   canon_white (nodename);
  4180.   for (i = 0; i < strlen (nodename); i++)
  4181.     {
  4182.       if (nodename[i] == '\t' ||
  4183.       nodename[i] == '.' ||
  4184.       nodename[i] == ',')
  4185.     {
  4186.       nodename[i] = '\0';
  4187.       break;
  4188.     }
  4189.     }
  4190. save_node:
  4191.   normalize_node_name (nodename);
  4192.   i = strlen (nodename);
  4193.   if (i && nodename[i - 1] == ':')
  4194.     nodename[i - 1] = '\0';
  4195.  
  4196.   remember_node_reference (nodename, line_number, menu_reference);
  4197.   free (nodename);
  4198.   input_text_offset = orig_offset;
  4199. }
  4200.  
  4201. void
  4202. cm_menu ()
  4203. {
  4204.   begin_insertion (menu);
  4205. }
  4206.  
  4207.  
  4208. /* **************************************************************** */
  4209. /*                                    */
  4210. /*            Cross Reference Hacking                */
  4211. /*                                    */
  4212. /* **************************************************************** */
  4213.  
  4214. char *
  4215. get_xref_token ()
  4216. {
  4217.   char *string;
  4218.  
  4219.   get_until_in_braces (",", &string);
  4220.   if (curchar () == ',')
  4221.     input_text_offset++;
  4222.   fix_whitespace (string);
  4223.   normalize_node_name (string);
  4224.   return (string);
  4225. }
  4226.  
  4227. int px_ref_flag = 0;        /* Controls initial output string. */
  4228.  
  4229. /* Make a cross reference. */
  4230. void
  4231. cm_xref (arg)
  4232. int arg;
  4233. {
  4234.   if (arg == START)
  4235.     {
  4236.       char *arg1, *arg2, *arg3, *arg4, *arg5;
  4237.  
  4238.       arg1 = get_xref_token ();
  4239.       arg2 = get_xref_token ();
  4240.       arg3 = get_xref_token ();
  4241.       arg4 = get_xref_token ();
  4242.       arg5 = get_xref_token ();
  4243.  
  4244.       add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
  4245.  
  4246.       if (*arg5 || *arg4)
  4247.     {
  4248.       char *node_name;
  4249.  
  4250.       if (!*arg2)
  4251.         node_name = arg1;
  4252.       else
  4253.         node_name = arg2;
  4254.  
  4255.       add_word_args ("%s: (%s)%s", arg2, arg4, arg1);
  4256.       return;
  4257.     }
  4258.       else
  4259.     remember_node_reference (arg1, line_number, followed_reference);
  4260.  
  4261.       if (*arg3)
  4262.     {
  4263.       if (!*arg2)
  4264.         {
  4265.           add_word_args ("%s: %s", arg3, arg1);
  4266.         }
  4267.       else
  4268.         {
  4269.           add_word_args ("%s: %s", arg2, arg1);
  4270.         }
  4271.       return;
  4272.     }
  4273.  
  4274.       if (*arg2)
  4275.     {
  4276.       execute_string ("%s", arg2);
  4277.       add_word_args (": %s", arg1);
  4278.     }
  4279.       else
  4280.     {
  4281.       add_word_args ("%s::", arg1);
  4282.     }
  4283.  
  4284.     }
  4285.   else
  4286.     {
  4287.  
  4288.       /* Check to make sure that the next non-whitespace character is either
  4289.          a period or a comma. input_text_offset is pointing at the "}" which
  4290.          ended the xref or pxref command. */
  4291.  
  4292.       size_t temp = input_text_offset + 1;
  4293.  
  4294.       if (output_paragraph[output_paragraph_offset - 2] == ':' &&
  4295.       output_paragraph[output_paragraph_offset - 1] == ':')
  4296.     return;
  4297.       while (temp < size_of_input_text)
  4298.     {
  4299.       if (cr_or_whitespace (input_text[temp]))
  4300.         temp++;
  4301.       else
  4302.         {
  4303.           if (input_text[temp] == '.' ||
  4304.           input_text[temp] == ',' ||
  4305.           input_text[temp] == '\t')
  4306.         return;
  4307.           else
  4308.         {
  4309.           line_error (
  4310.             "Cross-reference must be terminated with a period or a comma");
  4311.           return;
  4312.         }
  4313.         }
  4314.     }
  4315.     }
  4316. }
  4317.  
  4318. void
  4319. cm_pxref (arg)
  4320.      int arg;
  4321. {
  4322.   if (arg == START)
  4323.     {
  4324.       px_ref_flag++;
  4325.       cm_xref (arg);
  4326.       px_ref_flag--;
  4327.     }
  4328.   else
  4329.     add_char ('.');
  4330. }
  4331.  
  4332. void
  4333. cm_inforef (arg)
  4334.      int arg;
  4335. {
  4336.   if (arg == START)
  4337.     {
  4338.       char *node, *pname, *file;
  4339.  
  4340.       node = get_xref_token ();
  4341.       pname = get_xref_token ();
  4342.       file = get_xref_token ();
  4343.  
  4344.       add_word_args ("*note %s: (%s)%s", pname, file, node);
  4345.     }
  4346. }
  4347.  
  4348. /* **************************************************************** */
  4349. /*                                    */
  4350. /*            Insertion Command Stubs                */
  4351. /*                                    */
  4352. /* **************************************************************** */
  4353.  
  4354. void
  4355. cm_quotation ()
  4356. {
  4357.   begin_insertion (quotation);
  4358. }
  4359.  
  4360. void
  4361. cm_example ()
  4362. {
  4363.   begin_insertion (example);
  4364. }
  4365.  
  4366. void
  4367. cm_smallexample ()
  4368. {
  4369.   begin_insertion (smallexample);
  4370. }
  4371.  
  4372. void
  4373. cm_lisp ()
  4374. {
  4375.   begin_insertion (lisp);
  4376. }
  4377.  
  4378. void
  4379. cm_format ()
  4380. {
  4381.   begin_insertion (format);
  4382. }
  4383.  
  4384. void
  4385. cm_display ()
  4386. {
  4387.   begin_insertion (display);
  4388. }
  4389.  
  4390. void
  4391. cm_itemize ()
  4392. {
  4393.   begin_insertion (itemize);
  4394. }
  4395.  
  4396. void
  4397. cm_enumerate ()
  4398. {
  4399.   begin_insertion (enumerate);
  4400. }
  4401.  
  4402. void
  4403. cm_table ()
  4404. {
  4405.   begin_insertion (table);
  4406. }
  4407.  
  4408. void
  4409. cm_group ()
  4410. {
  4411.   begin_insertion (group);
  4412. }
  4413.  
  4414. void
  4415. cm_ifinfo ()
  4416. {
  4417.   begin_insertion (ifinfo);
  4418. }
  4419.  
  4420. void
  4421. cm_tex ()
  4422. {
  4423.   discard_until ("\n@end tex");
  4424.   discard_until ("\n");
  4425. }
  4426.  
  4427. void
  4428. cm_iftex ()
  4429. {
  4430.   discard_until ("\n@end iftex");
  4431.   discard_until ("\n");
  4432. }
  4433.  
  4434. void
  4435. cm_titlespec ()
  4436. {
  4437.   discard_until ("\n@end titlespec");
  4438.   discard_until ("\n");
  4439. }
  4440.  
  4441. void
  4442. cm_titlepage ()
  4443. {
  4444.   discard_until ("\n@end titlepage");
  4445.   discard_until ("\n");
  4446. }
  4447.  
  4448. void
  4449. cm_ignore ()
  4450. {
  4451.   discard_until ("\n@end ignore");
  4452.   discard_until ("\n");
  4453. }
  4454.  
  4455. /* **************************************************************** */
  4456. /*                                    */
  4457. /*            @itemx, @item                    */
  4458. /*                                    */
  4459. /* **************************************************************** */
  4460.  
  4461. /* Non-zero means a string is in execution, as opposed to a file. */
  4462. int executing_string = 0;
  4463.  
  4464. /* Execute the string produced by formatting the ARGs with FORMAT.  This
  4465.    is like submitting a new file with @include. */
  4466. void
  4467. execute_string (const char *format, ...)
  4468. {
  4469.  
  4470.   va_list args;
  4471.   static char temp_string[4000];
  4472.   va_start(args, format);
  4473.   vsprintf (temp_string, format, args);
  4474.   va_end(args);
  4475.   strcat (temp_string, "@bye\n");
  4476.   pushfile ();
  4477.   input_text_offset = 0;
  4478.   input_text = temp_string;
  4479.   input_filename = savestring (input_filename);
  4480.   size_of_input_text = strlen (temp_string);
  4481.  
  4482.   executing_string++;
  4483.   reader_loop ();
  4484.  
  4485.   popfile ();
  4486.   executing_string--;
  4487.  
  4488.   free_and_clear (&command);
  4489.   command = savestring ("not bye");
  4490. }
  4491.  
  4492. int itemx_flag = 0;
  4493.  
  4494. void
  4495. cm_itemx ()
  4496. {
  4497.   itemx_flag++;
  4498.   cm_item ();
  4499.   itemx_flag--;
  4500. }
  4501.  
  4502.  
  4503. void
  4504. cm_item ()
  4505. {
  4506.   char *rest_of_line, *item_func;
  4507.  
  4508.   /* Can only hack "@item" while inside of an insertion. */
  4509.   if (insertion_level)
  4510.     {
  4511.       get_until (EOL, &rest_of_line);
  4512.       canon_white (rest_of_line);
  4513.       item_func = current_item_function ();
  4514.  
  4515.       /* Okay, do the right thing depending on which insertion function
  4516.      is active. */
  4517.  
  4518.       switch (current_insertion_type ())
  4519.     {
  4520.     case menu:
  4521.     case quotation:
  4522.     case example:
  4523.     case smallexample:
  4524.     case lisp:
  4525.     case format:
  4526.     case display:
  4527.     case group:
  4528.     case ifinfo:
  4529.       line_error ("The `@%s' command is meaningless within a `@%s' block",
  4530.               command,
  4531.               insertion_type_pname (current_insertion_type ()));
  4532.       break;
  4533.  
  4534.     case itemize:
  4535.     case enumerate:
  4536.       if (itemx_flag)
  4537.         {
  4538.           line_error ("@itemx is not meaningful inside of a `%s' block",
  4539.               insertion_type_pname (current_insertion_type ()));
  4540.         }
  4541.       else
  4542.         {
  4543.           start_paragraph ();
  4544.           kill_self_indent (-1);
  4545.           discard_until ("\n");
  4546.           filling_enabled = indented_fill = true;
  4547.  
  4548.           if (current_insertion_type () == itemize)
  4549.         {
  4550.           indent (output_column = current_indent - 2);
  4551.  
  4552.           /* I need some way to determine whether this command
  4553.              takes braces or not.  I believe the user can type
  4554.              either "@bullet" or "@bullet{}".  Of course, they
  4555.              can also type "o" or "#" or whatever else they want. */
  4556.           if (item_func && *item_func)
  4557.             {
  4558.               if (*item_func == '@')
  4559.             if (item_func[strlen (item_func) - 1] != '}')
  4560.               execute_string ("%s{}", item_func);
  4561.             else
  4562.               execute_string ("%s", item_func);
  4563.               else
  4564.             execute_string ("%s", item_func);
  4565.             }
  4566.           insert (' ');
  4567.           output_column++;
  4568.         }
  4569.           else
  4570.         number_item ();
  4571.  
  4572.           /* Special hack.  This makes close paragraph ignore you until
  4573.          the start_paragraph () function has been called. */
  4574.           must_start_paragraph = 1;
  4575.         }
  4576.       break;
  4577.  
  4578.     case table:
  4579.       {
  4580.         /* Get rid of extra characters. */
  4581.         kill_self_indent (-1);
  4582.  
  4583.         /* close_paragraph () almost does what we want.  The problem
  4584.            is when paragraph_is_open, and last_char_was_newline, and
  4585.            the last newline has been turned into a space, because
  4586.            filling_enabled. I handle it here. */
  4587.         if (last_char_was_newline && filling_enabled && paragraph_is_open)
  4588.           insert ('\n');
  4589.         close_paragraph ();
  4590.  
  4591.         /* Indent on a new line, but back up one indentation level. */
  4592.         /* force existing indentation. */
  4593.         add_char ('i');
  4594.         output_paragraph_offset--;
  4595.         kill_self_indent (default_indentation_increment + 1);
  4596.  
  4597.         /* Add item's argument to the line. */
  4598.         filling_enabled = false;
  4599.         if (!item_func && !(*item_func))
  4600.           execute_string ("%s", rest_of_line);
  4601.         else
  4602.           execute_string ("%s{%s}", item_func, rest_of_line);
  4603.  
  4604.         /* Start a new line, and let start_paragraph ()
  4605.            do the indenting of it for you. */
  4606.         close_single_paragraph ();
  4607.         indented_fill = filling_enabled = true;
  4608.       }
  4609.         default:
  4610.       break;
  4611.     }
  4612.       free (rest_of_line);
  4613.     }
  4614.   else
  4615.     line_error ("@%s found outside of an insertion block", command);
  4616. }
  4617.  
  4618.  
  4619. /* **************************************************************** */
  4620. /*                                    */
  4621. /*            Defun and Friends                   */
  4622. /*                                    */
  4623. /* **************************************************************** */
  4624.  
  4625. /* The list of args that were passed to the def**** command. */
  4626. char **defun_args = (char **)NULL;
  4627.  
  4628. /* An alist mapping defun insertion types to the text that we use
  4629.    to describe them. */
  4630. struct {
  4631.   enum insertion_type type;
  4632.   char *title;
  4633. } type_title_alist[] = {
  4634.   { defun, "Function" },
  4635.   { defmac, "Macro" },
  4636.   { defspec, "Special form" },
  4637.   { defopt, "Option" },
  4638.   { deffn, (char *)NULL },
  4639.   { defvar, "Variable" },
  4640.   { (enum insertion_type)0, (char *)NULL }
  4641. };
  4642.  
  4643. /* Return the title string for this type of defun. */
  4644. char *
  4645. defun_title (type)
  4646.      enum insertion_type type;
  4647. {
  4648.   register int i;
  4649.  
  4650.   for (i = 0; type_title_alist[i].type || type_title_alist[i].title; i++)
  4651.     if (type_title_alist[i].type == type)
  4652.       return (type_title_alist[i].title);
  4653.   return (char *)NULL;
  4654. }
  4655.  
  4656. /* Return a list of words from the contents of STRING.
  4657.    You can group words with braces. */
  4658. char **
  4659. args_from_string (string)
  4660.      char *string;
  4661. {
  4662.   char **result = (char **) NULL;
  4663.   register int i, start, result_index, size;
  4664.   int len, skip_til_brace = 0;
  4665.  
  4666.   i = result_index = size = 0;
  4667.  
  4668.   /* Get a token, and stuff it into RESULT.  The tokens are split
  4669.      at spaces or tabs. */
  4670.   while (string[i])
  4671.     {
  4672.       /* Skip leading whitespace. */
  4673.       for (; string[i] && whitespace (string[i]); i++);
  4674.  
  4675.       start = i;
  4676.  
  4677.       if (!string[i])
  4678.     return (result);
  4679.  
  4680.       /* If this is a command which takes it's argument in braces, then
  4681.      gobble the whole thing. */
  4682.       if (string[i] == COMMAND_PREFIX)
  4683.     {
  4684.       register int j;
  4685.       for (j = i; string[j] &&
  4686.            !whitespace (string[j]) &&
  4687.            string[j] != '{'; j++);
  4688.  
  4689.       if (string[j] == '{')
  4690.         {
  4691.           while (string[j] && string[j] != '}')
  4692.         j++;
  4693.  
  4694.           if (string[j])
  4695.         j++;
  4696.  
  4697.           i = j;
  4698.           goto add_arg;
  4699.         }
  4700.     }
  4701.       
  4702.       if (string[i] == '{' && !whitespace (string[i + 1]))
  4703.     {
  4704.       skip_til_brace = 1;
  4705.       start = ++i;
  4706.     }
  4707.  
  4708.       /* Skip until whitespace or close brace. */
  4709.       while (string[i] &&
  4710.          ((skip_til_brace && string[i] != '}') ||
  4711.           (!skip_til_brace && !whitespace (string[i]))))
  4712.     i++;
  4713.  
  4714.     add_arg:
  4715.       len = i - start;
  4716.       if (result_index + 2 >= size)
  4717.     {
  4718.       if (!size)
  4719.         result = (char **) xmalloc ((size = 10) * (sizeof (char *)));
  4720.       else
  4721.         result =
  4722.           (char **) xrealloc (result, ((size += 10) * (sizeof (char *))));
  4723.     }
  4724.       result[result_index] = (char *) xmalloc (1 + len);
  4725.       strncpy (result[result_index], string + start, len);
  4726.       result[result_index][len] = '\0';
  4727.       result_index++;
  4728.       result[result_index] = (char *) NULL;
  4729.  
  4730.       if (skip_til_brace)
  4731.     {
  4732.       skip_til_brace = 0;
  4733.       if (string[i])
  4734.         i++;
  4735.     }
  4736.     }
  4737.  
  4738.   return (result);
  4739. }
  4740.  
  4741. void
  4742. get_defun_args ()
  4743. {
  4744.   register int i;
  4745.   char *line;
  4746.  
  4747.   get_rest_of_line (&line);
  4748.  
  4749.   if (defun_args)
  4750.     {
  4751.       for (i = 0; defun_args[i]; i++)
  4752.     free (defun_args[i]);
  4753.       free (defun_args);
  4754.     }
  4755.  
  4756.   defun_args = args_from_string (line);
  4757.   free (line);
  4758. }
  4759.  
  4760. void
  4761. insert_defun_arg (string, where)
  4762.      char *string;
  4763.      int where;
  4764. {
  4765.   register int i;
  4766.  
  4767.   for (i = 0; defun_args[i]; i++);
  4768.   defun_args = (char **)xrealloc (defun_args, (i + 2) * sizeof (char *));
  4769.   defun_args[i + 1] = (char *)NULL;
  4770.  
  4771.   for (; i != where; --i)
  4772.     defun_args[i] = defun_args[i - 1];
  4773.  
  4774.   defun_args[i] = savestring (string);
  4775. }
  4776.  
  4777. /* Make the defun type insertion.
  4778.    TYPE says which insertion this is.
  4779.    TITLE is the string to describe the object being described, or NULL
  4780.    for no title string.
  4781.    X_P says not to start a new insertion if non-zero. */
  4782. void
  4783. defun_internal (type, title, x_p)
  4784.      enum insertion_type type;
  4785.      char *title;
  4786.      int x_p;
  4787. {
  4788.   register int i = 0;
  4789.   char *type_name, *func_name = "";
  4790.   int old_no_indent = no_indent;
  4791.  
  4792.   get_defun_args ();
  4793.  
  4794.   if (title)
  4795.     insert_defun_arg (title, 0);
  4796.  
  4797.   if (defun_args[0])
  4798.     {
  4799.       type_name = defun_args[0];
  4800.       i++;
  4801.  
  4802.       if (defun_args[1])
  4803.     {
  4804.       func_name = defun_args[1];
  4805.       i++;
  4806.     }
  4807.     }
  4808.   else
  4809.     type_name = "";
  4810.  
  4811.   no_indent = true;
  4812.   start_paragraph ();
  4813.   execute_string (" * %s: %s", type_name, func_name);
  4814.   no_indent = old_no_indent;
  4815.  
  4816.   for (; defun_args[i]; i++)
  4817.     {
  4818.       if (*defun_args[i] == '&')
  4819.     add_word_args (" %s", defun_args[i]);
  4820.       else
  4821.     execute_string (" @var{%s}", defun_args[i]);
  4822.     }
  4823.   add_char ('\n');
  4824.   if (type == defvar || type == defopt)
  4825.     execute_string ("@vindex %s\n", func_name);
  4826.   else
  4827.     execute_string ("@findex %s\n", func_name);
  4828.  
  4829.   if (!x_p)
  4830.     begin_insertion (type);
  4831.   else
  4832.     start_paragraph ();
  4833. }
  4834.  
  4835. /* Add an entry for a function, macro, special form, variable, or option.
  4836.    If the name of the calling command ends in `x', then this is an extra
  4837.    entry included in the body of an insertion of the same type. */
  4838. void
  4839. cm_defun ()
  4840. {
  4841.   int x_p;
  4842.   enum insertion_type type;
  4843.   char *title, *temp = savestring (command);
  4844.  
  4845.   x_p = (command[strlen (command) - 1] == 'x');
  4846.  
  4847.   if (x_p)
  4848.     temp[strlen (temp) - 1] = '\0';
  4849.  
  4850.   type = find_type_from_name (temp);
  4851.   free (temp);
  4852.  
  4853.   /* If we are adding to an already existing insertion, then make sure
  4854.      that we are already in an insertion of type TYPE. */
  4855.   if (x_p &&
  4856.       (!insertion_level || insertion_stack->insertion != type))
  4857.     {
  4858.       line_error ("Must be in a `%s' insertion in order to use `%s'x",
  4859.           command, command);
  4860.       return;
  4861.     }
  4862.  
  4863.   title = defun_title (type);
  4864.   defun_internal (type, title, x_p);
  4865. }
  4866.  
  4867. /* End existing insertion block. */
  4868. void
  4869. cm_end ()
  4870. {
  4871.   char *temp;
  4872.   enum insertion_type type;
  4873.  
  4874.   if (!insertion_level)
  4875.     {
  4876.       line_error ("Unmatched `@%s'", command);
  4877.       return;
  4878.     }
  4879.   get_rest_of_line (&temp);
  4880.   canon_white (temp);
  4881.  
  4882.   if (strlen (temp) == 0)
  4883.     line_error ("`@%s' needs something after it", command);
  4884.   type = find_type_from_name (temp);
  4885.   if (type == bad_type)
  4886.     {
  4887.       line_error ("Bad argument to `%s', `%s', using `%s'",
  4888.        command, temp, insertion_type_pname (current_insertion_type ()));
  4889.     }
  4890.   end_insertion (type);
  4891.   free (temp);
  4892. }
  4893.  
  4894.  
  4895. /* **************************************************************** */
  4896. /*                                    */
  4897. /*            Other Random Commands                   */
  4898. /*                                    */
  4899. /* **************************************************************** */
  4900.  
  4901. /* noindent () used to do something valueable, but it didn't follow the
  4902.    spec for noindent in the texinfo manual.  Now it does nothing, which,
  4903.    in the case of makeinfo, is correct. */
  4904. void
  4905. cm_noindent ()
  4906. {
  4907. /*  no_indent = true;
  4908.   indented_fill = false; */
  4909. }
  4910.  
  4911. /* I don't know exactly what to do with this.  Should I allow
  4912.    someone to switch filenames in the middle of output?  Since the
  4913.    file could be partially written, this doesn't seem to make sense.
  4914.    Another option: ignore it, since they don't *really* want to
  4915.    switch files.  Finally, complain, or at least warn. */
  4916. void
  4917. cm_setfilename ()
  4918. {
  4919.   char *filename;
  4920.   get_rest_of_line (&filename);
  4921.   /* warning ("`@%s %s' encountered and ignored", command, filename); */
  4922.   free (filename);
  4923. }
  4924.  
  4925. void
  4926. cm_comment ()
  4927. {
  4928.   discard_until ("\n");
  4929. }
  4930.  
  4931. void
  4932. cm_br ()
  4933. {
  4934.   close_paragraph ();
  4935. }
  4936.  
  4937.  /* Insert the number of blank lines passed as argument. */
  4938. void
  4939. cm_sp ()
  4940. {
  4941.   int lines;
  4942.   char *line;
  4943.  
  4944. /*  close_paragraph (); */
  4945.   get_rest_of_line (&line);
  4946.  
  4947.   sscanf (line, "%d", &lines);
  4948.   while (lines--)
  4949.     add_char ('\n');
  4950.   free (line);
  4951. }
  4952.  
  4953. void
  4954. cm_settitle ()
  4955. {
  4956.   discard_until ("\n");
  4957. }
  4958.  
  4959. /**
  4960. void
  4961. cm_need ()
  4962. {
  4963. }
  4964. **/
  4965.  
  4966. /* Start a new line with just this text on it.
  4967.    Then center the line of text.
  4968.    This always ends the current paragraph. */
  4969. void
  4970. cm_center ()
  4971. {
  4972.   char *line;
  4973.   int len;
  4974.  
  4975.   close_paragraph ();
  4976.   filling_enabled = indented_fill = false;
  4977.  
  4978.   get_rest_of_line (&line);
  4979.  
  4980.   if ((len = strlen (line)) < fill_column)
  4981.     {
  4982.       int i = (fill_column - len) / 2;
  4983.       while (i--)
  4984.     insert (' ');
  4985.     }
  4986.   paragraph_is_open=true;
  4987.   execute_string (line);
  4988.   free (line);
  4989.   insert ('\n');
  4990.   close_paragraph ();
  4991.   filling_enabled = true;
  4992. }
  4993.  
  4994. /* Show what an expression returns. */
  4995. void
  4996. cm_result (arg)
  4997.      int arg;
  4998. {
  4999.   if (arg == END)
  5000.     add_word ("=>");
  5001. }
  5002.  
  5003. /* What an expression expands to. */
  5004. void
  5005. cm_expansion (arg)
  5006.      int arg;
  5007. {
  5008.   if (arg == END)
  5009.     add_word ("==>");
  5010. }
  5011.  
  5012. /* Indicates two expressions are equivalent. */
  5013. void
  5014. cm_equiv (arg)
  5015.      int arg;
  5016. {
  5017.   if (arg == END)
  5018.     add_word ("==");
  5019. }
  5020.  
  5021. /* What an expression may print. */
  5022. void
  5023. cm_print (arg)
  5024.      int arg;
  5025. {
  5026.   if (arg == END)
  5027.     add_word ("-|");
  5028. }
  5029.  
  5030. /* An error signaled. */
  5031. void
  5032. cm_error (arg)
  5033.      int arg;
  5034. {
  5035.   if (arg == END)
  5036.     add_word ("error-->");
  5037. }
  5038.  
  5039. /* The location of point in an example of a buffer. */
  5040. void
  5041. cm_point (arg)
  5042.      int arg;
  5043. {
  5044.   if (arg == END)
  5045.     add_word ("-!-");
  5046. }
  5047.  
  5048. /* Start a new line with just this text on it.
  5049.    The text is outdented one level if possible. */
  5050. void
  5051. cm_exdent ()
  5052. {
  5053.   char *line;
  5054.   int i = current_indent;
  5055.  
  5056.   if (current_indent)
  5057.     current_indent -= default_indentation_increment;
  5058.  
  5059.   get_rest_of_line (&line);
  5060.   close_single_paragraph ();
  5061.   add_word_args ("%s", line);
  5062.   current_indent = i;
  5063.   free (line);
  5064.   close_single_paragraph ();
  5065. }
  5066.  
  5067. void
  5068. cm_include ()
  5069. {
  5070.   cm_infoinclude ();
  5071. }
  5072.  
  5073. /* Remember this file, and move onto the next. */
  5074. void
  5075. cm_infoinclude ()
  5076. {
  5077.   char *filename;
  5078.  
  5079.   close_paragraph ();
  5080.   get_rest_of_line (&filename);
  5081.   pushfile ();
  5082.  
  5083.   /* In verbose mode we print info about including another file. */
  5084.   if (verbose_mode)
  5085.     {
  5086.       register int i = 0;
  5087.       register FSTACK *stack = filestack;
  5088.  
  5089.       for (i = 0, stack = filestack; stack; stack = stack->next, i++);
  5090.  
  5091.       i *= 2;
  5092.  
  5093.       printf ("%*s", i, "");
  5094.       printf ("%c%s %s\n", COMMAND_PREFIX, command, filename);
  5095.       fflush (stdout);
  5096.     }
  5097.  
  5098.   if (!find_and_load (filename))
  5099.     {
  5100.       extern char *sys_errlist[];
  5101.       extern int errno, sys_nerr;
  5102.       popfile ();
  5103.  
  5104.       /* Cannot "@include foo", in line 5 of "/wh/bar". */
  5105.       line_error ("`%c%s %s': %s", COMMAND_PREFIX, command, filename,
  5106.           ((errno < sys_nerr) ?
  5107.            sys_errlist[errno] : "Unknown file system error"));
  5108.     }
  5109.   free (filename);
  5110. }
  5111.  
  5112. /* The other side of a malformed expression. */
  5113. void
  5114. misplaced_brace ()
  5115. {
  5116.   line_error ("Misplaced `}'");
  5117. }
  5118.  
  5119. /* Don't let the filling algorithm insert extra whitespace here. */
  5120. /**
  5121. void
  5122. cm_force_abbreviated_whitespace ()
  5123. {
  5124. }
  5125. **/
  5126.  
  5127. /* Make the output paragraph end the sentence here, even though it
  5128.    looks like it shouldn't.  This also inserts the character which
  5129.    invoked it. */
  5130. void
  5131. cm_force_sentence_end ()
  5132. {
  5133.   add_char (META ((*command)));
  5134. }
  5135.  
  5136. /* Signals end of processing.  Easy to make this happen. */
  5137. void
  5138. cm_bye ()
  5139. {
  5140.   input_text_offset = size_of_input_text;
  5141. }
  5142.  
  5143. /**
  5144. void
  5145. cm_asis ()
  5146. {
  5147. }
  5148. **/
  5149.  
  5150. void
  5151. cm_setchapternewpage ()
  5152. {
  5153.   discard_until ("\n");
  5154. }
  5155.  
  5156. void
  5157. cm_smallbook ()
  5158. {
  5159.   discard_until ("\n");
  5160. }
  5161.  
  5162. /* **************************************************************** */
  5163. /*                                    */
  5164. /*            Indexing Stuff                    */
  5165. /*                                    */
  5166. /* **************************************************************** */
  5167.  
  5168.  
  5169. INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL;
  5170.  
  5171. /* An array of pointers.  Each one is for a different index.  The
  5172.    "synindex" command changes which array slot is pointed to by a
  5173.    given "index". */
  5174. INDEX_ELT **the_indices = (INDEX_ELT **) NULL;
  5175.  
  5176. /* The number of defined indices. */
  5177. int defined_indices = 0;
  5178.  
  5179. /* We predefine these. */
  5180. #define program_index 0
  5181. #define function_index 1
  5182. #define concept_index 2
  5183. #define variable_index 3
  5184. #define datatype_index 4
  5185. #define key_index 5
  5186.  
  5187. void
  5188. init_indices ()
  5189. {
  5190.   int i;
  5191.  
  5192.   /* Create the default data structures. */
  5193.  
  5194.   /* Initialize data space. */
  5195.   if (!the_indices)
  5196.     {
  5197.       the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) *
  5198.                         sizeof (INDEX_ELT *));
  5199.       the_indices[defined_indices] = (INDEX_ELT *) NULL;
  5200.  
  5201.       name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) *
  5202.                            sizeof (INDEX_ALIST *));
  5203.       name_index_alist[defined_indices] = (INDEX_ALIST *) NULL;
  5204.     }
  5205.  
  5206.   /* If there were existing indices, get rid of them now. */
  5207.   for (i = 0; i < defined_indices; i++)
  5208.     (void) undefindex (name_index_alist[i]->name);
  5209.  
  5210.   /* Add the default indices. */
  5211.   defindex ("pg", 0);
  5212.   defindex ("fn", 1);        /* "fn" is a code index.  */
  5213.   defindex ("cp", 0);
  5214.   defindex ("vr", 0);
  5215.   defindex ("tp", 0);
  5216.   defindex ("ky", 0);
  5217.  
  5218. }
  5219.  
  5220. /* Find which element in the known list of indices has this name.
  5221.    Returns -1 if NAME isn't found. */
  5222. int
  5223. find_index_offset (name)
  5224.      char *name;
  5225. {
  5226.   register int i;
  5227.   for (i = 0; i < defined_indices; i++)
  5228.     if (name_index_alist[i] &&
  5229.     stricmp (name, name_index_alist[i]->name) == 0)
  5230.       return (name_index_alist[i]->index);
  5231.   return (-1);
  5232. }
  5233.  
  5234. /* Return a pointer to the entry of (name . index) for this name.
  5235.    Return NULL if the index doesn't exist. */
  5236. INDEX_ALIST *
  5237. find_index (name)
  5238.      char *name;
  5239. {
  5240.   int offset = find_index_offset (name);
  5241.   if (offset > -1)
  5242.     return (name_index_alist[offset]);
  5243.   else
  5244.     return ((INDEX_ALIST *) NULL);
  5245. }
  5246.  
  5247. /* Given an index name, return the offset in the_indices of this index,
  5248.    or -1 if there is no such index. */
  5249. int
  5250. translate_index (name)
  5251.      char *name;
  5252. {
  5253.   INDEX_ALIST *which = find_index (name);
  5254.  
  5255.   if (which)
  5256.     return (which->index);
  5257.   else
  5258.     return (-1);
  5259. }
  5260.  
  5261. /* Return the index list which belongs to NAME. */
  5262. INDEX_ELT *
  5263. index_list (name)
  5264.      char *name;
  5265. {
  5266.   int which = translate_index (name);
  5267.   if (which < 0)
  5268.     return ((INDEX_ELT *) - 1);
  5269.   else
  5270.     return (the_indices[which]);
  5271. }
  5272.  
  5273. /* Please release me, let me go... */
  5274. void
  5275. free_index (index)
  5276.      INDEX_ELT *index;
  5277. {
  5278.   INDEX_ELT *temp;
  5279.  
  5280.   while ((temp = index) != (INDEX_ELT *) NULL)
  5281.     {
  5282.       free (temp->entry);
  5283.       free (temp->node);
  5284.       index = index->next;
  5285.       free (temp);
  5286.     }
  5287. }
  5288.  
  5289. /* Flush an index by name. */
  5290. int
  5291. undefindex (name)
  5292.      char *name;
  5293. {
  5294.   int i;
  5295.   int which = find_index_offset (name);
  5296.  
  5297.   if (which < 0)
  5298.     return (which);
  5299.  
  5300.   i = name_index_alist[which]->index;
  5301.  
  5302.  
  5303.   free_index (the_indices[i]);
  5304.   the_indices[i] = (INDEX_ELT *) NULL;
  5305.  
  5306.   free (name_index_alist[which]->name);
  5307.   free (name_index_alist[which]);
  5308.   name_index_alist[which] = (INDEX_ALIST *) NULL;
  5309.   return (which);
  5310. }
  5311.  
  5312. /* Define an index known as NAME.  We assign the slot number.
  5313.    CODE if non-zero says to make this a code index. */
  5314. void
  5315. defindex (name, code)
  5316.      char *name;
  5317.      int code;
  5318. {
  5319.   register int i, slot;
  5320.  
  5321.   /* If it already exists, flush it. */
  5322.   undefindex (name);
  5323.  
  5324.   /* Try to find an empty slot. */
  5325.   slot = -1;
  5326.   for (i = 0; i < defined_indices; i++)
  5327.     if (!name_index_alist[i])
  5328.       {
  5329.     slot = i;
  5330.     break;
  5331.       }
  5332.  
  5333.   if (slot < 0)
  5334.     {
  5335.       /* No such luck.  Make space for another index. */
  5336.       slot = defined_indices;
  5337.       defined_indices++;
  5338.  
  5339.       name_index_alist = (INDEX_ALIST **) xrealloc (name_index_alist,
  5340.                             (1 + defined_indices)
  5341.                           * sizeof (INDEX_ALIST *));
  5342.       the_indices = (INDEX_ELT **) xrealloc (the_indices,
  5343.                          (1 + defined_indices)
  5344.                          * sizeof (INDEX_ELT *));
  5345.     }
  5346.  
  5347.   /* We have a slot.  Start assigning. */
  5348.   name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST));
  5349.   name_index_alist[slot]->name = savestring (name);
  5350.   name_index_alist[slot]->index = slot;
  5351.   name_index_alist[slot]->code = code;
  5352.  
  5353.   the_indices[slot] = (INDEX_ELT *) NULL;
  5354. }
  5355.  
  5356. /* Add the arguments to the current index command to the index NAME. */
  5357. void
  5358. index_add_arg (name)
  5359.      char *name;
  5360. {
  5361.   int which;
  5362.   char *index_entry;
  5363.   INDEX_ALIST *tem;
  5364.  
  5365.   tem = find_index (name);
  5366.  
  5367.   which = tem ? tem->index : -1;
  5368.  
  5369.   /* close_paragraph (); */
  5370.   get_rest_of_line (&index_entry);
  5371.  
  5372.   if (which < 0)
  5373.     {
  5374.       line_error ("Unknown index reference `%s'", name);
  5375.       free (index_entry);
  5376.     }
  5377.   else
  5378.     {
  5379.       INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT));
  5380.       new->next = the_indices[which];
  5381.       new->entry = index_entry;
  5382.       new->node = current_node;
  5383.       new->code = tem->code;
  5384.       the_indices[which] = new;
  5385.     }
  5386. }
  5387.  
  5388. #define INDEX_COMMAND_SUFFIX "index"
  5389.  
  5390. /* The function which user defined index commands call. */
  5391. void
  5392. gen_index ()
  5393. {
  5394.   char *name = savestring (command);
  5395.   size_t len;
  5396.  
  5397.   len = strlen (name);
  5398.   if (len >= sizeof ("index"))
  5399.     name[len - sizeof ("index")] = '\0';
  5400.   index_add_arg (name);
  5401.   free (name);
  5402. }
  5403.  
  5404. /* Define a new index command.  Arg is name of index. */
  5405. void
  5406. cm_defindex ()
  5407. {
  5408.   gen_defindex (0);
  5409. }
  5410.  
  5411. void
  5412. cm_defcodeindex ()
  5413. {
  5414.   gen_defindex (1);
  5415. }
  5416.  
  5417. void
  5418. gen_defindex (code)
  5419.      int code;
  5420. {
  5421.   char *name;
  5422.   get_rest_of_line (&name);
  5423.  
  5424.   if (find_index (name))
  5425.     {
  5426.       line_error ("Index `%s' already exists", name);
  5427.       free (name);
  5428.       return;
  5429.     }
  5430.   else
  5431.     {
  5432.       char *temp = (char *) alloca (1 + strlen (name) + sizeof ("index"));
  5433.       sprintf (temp, "%sindex", name);
  5434.       define_user_command (temp, gen_index, 0);
  5435.       defindex (name, code);
  5436.       free (name);
  5437.     }
  5438. }
  5439.  
  5440. /* Append LIST2 to LIST1.  Return the head of the list. */
  5441. INDEX_ELT *
  5442. index_append (head, tail)
  5443.      INDEX_ELT *head, *tail;
  5444. {
  5445.   register INDEX_ELT *t_head = head;
  5446.  
  5447.   if (!t_head)
  5448.     return (tail);
  5449.  
  5450.   while (t_head->next)
  5451.     t_head = t_head->next;
  5452.   t_head->next = tail;
  5453.   return (head);
  5454. }
  5455.  
  5456. /* Expects 2 args, on the same line.  Both are index abbreviations.
  5457.    Make the first one be a synonym for the second one, i.e. make the
  5458.    first one have the same index as the second one. */
  5459. void
  5460. cm_synindex ()
  5461. {
  5462.   int redirector, redirectee;
  5463.   char *temp;
  5464.  
  5465.   skip_whitespace ();
  5466.   get_until_in_line (" ", &temp);
  5467.   redirectee = find_index_offset (temp);
  5468.   skip_whitespace ();
  5469.   free_and_clear (&temp);
  5470.   get_until_in_line (" ", &temp);
  5471.   redirector = find_index_offset (temp);
  5472.   free (temp);
  5473.   if (redirector < 0 || redirectee < 0)
  5474.     {
  5475.       line_error ("Unknown index reference");
  5476.     }
  5477.   else
  5478.     {
  5479.       /* I think that we should let the user make indices synonymous to
  5480.          each other without any lossage of info.  This means that one can
  5481.          say @synindex cp dt anywhere in the file, and things that used to
  5482.          be in cp will go into dt. */
  5483.       INDEX_ELT *i1 = the_indices[redirectee], *i2 = the_indices[redirector];
  5484.  
  5485.       if (i1 || i2)
  5486.     {
  5487.       if (i1)
  5488.         the_indices[redirectee] = index_append (i1, i2);
  5489.       else
  5490.         the_indices[redirectee] = index_append (i2, i1);
  5491.     }
  5492.  
  5493.       name_index_alist[redirectee]->index =
  5494.     name_index_alist[redirector]->index;
  5495.     }
  5496. }
  5497.  
  5498. void
  5499. cm_pindex ()            /* Pinhead index. */
  5500. {
  5501.   index_add_arg ("pg");
  5502. }
  5503.  
  5504. void
  5505. cm_vindex ()            /* variable index */
  5506. {
  5507.   index_add_arg ("vr");
  5508. }
  5509.  
  5510. void
  5511. cm_kindex ()            /* key index */
  5512. {
  5513.   index_add_arg ("ky");
  5514. }
  5515.  
  5516. void
  5517. cm_cindex ()            /* concept index */
  5518. {
  5519.   index_add_arg ("cp");
  5520. }
  5521.  
  5522. void
  5523. cm_findex ()            /* function index */
  5524. {
  5525.   index_add_arg ("fn");
  5526. }
  5527.  
  5528. void
  5529. cm_tindex ()            /* data type index */
  5530. {
  5531.   index_add_arg ("tp");
  5532. }
  5533.  
  5534. /* Sorting the index. */
  5535. int
  5536. index_element_compare (element1, element2)
  5537.      INDEX_ELT **element1, **element2;
  5538. {
  5539.   /* This needs to ignore leading non-text characters. */
  5540.   return (strcmp ((*element1)->entry, (*element2)->entry));
  5541. }
  5542.  
  5543. /* Sort the index passed in INDEX, returning an array of
  5544.    pointers to elements.  The array is terminated with a NULL
  5545.    pointer.  We call qsort because it's supposed to be fast.
  5546.    I think this looks bad. */
  5547. INDEX_ELT **
  5548. sort_index (index)
  5549.      INDEX_ELT *index;
  5550. {
  5551.   INDEX_ELT *temp = index;
  5552.   INDEX_ELT **array;
  5553.   int count = 0;
  5554.  
  5555.   while (temp != (INDEX_ELT *) NULL)
  5556.     {
  5557.       count++;
  5558.       temp = temp->next;
  5559.     }
  5560.  
  5561.   /* We have the length.  Make an array. */
  5562.  
  5563.   array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *));
  5564.   count = 0;
  5565.   temp = index;
  5566.  
  5567.   while (temp != (INDEX_ELT *) NULL)
  5568.     {
  5569.       array[count++] = temp;
  5570.       temp = temp->next;
  5571.     }
  5572.   array[count] = (INDEX_ELT *) NULL;    /* terminate the array. */
  5573.  
  5574.   /* Sort the array. */
  5575.   qsort (array, count, sizeof (INDEX_ELT *),
  5576.      (int (*)(const void *, const void *)) index_element_compare);
  5577.  
  5578.   return (array);
  5579. }
  5580.  
  5581. /* Non-zero means that we are in the middle of printing an index. */
  5582. int printing_index = 0;
  5583.  
  5584. /* Takes one arg, a short name of an index to print.
  5585.    Outputs a menu of the sorted elements of the index. */
  5586. void
  5587. cm_printindex ()
  5588. {
  5589.   int item;
  5590.   INDEX_ELT *index;
  5591.   INDEX_ELT **array;
  5592.   char *index_name;
  5593.   int old_inhibitions = inhibit_paragraph_indentation;
  5594.   boolean previous_filling_enabled_value = filling_enabled;
  5595.  
  5596.   close_paragraph ();
  5597.   get_rest_of_line (&index_name);
  5598.  
  5599.   index = index_list (index_name);
  5600.   if ((int) index < 0)
  5601.     {
  5602.       line_error ("Unknown index name `%s'", index_name);
  5603.       free (index_name);
  5604.       return;
  5605.     }
  5606.   else
  5607.     free (index_name);
  5608.  
  5609.   array = sort_index (index);
  5610.  
  5611.   filling_enabled = false;
  5612.   inhibit_paragraph_indentation = 1;
  5613.   close_paragraph ();
  5614.   add_word ("* Menu:\n\n");
  5615.  
  5616.   printing_index = 1;
  5617.   for (item = 0; (index = array[item]); item++)
  5618.     {
  5619.       /* If this particular entry should be printed as a "code" index,
  5620.      then wrap the entry with "@code{...}". */
  5621.       if (index->code)
  5622.     execute_string ("* @code{%s}: %s.\n", index->entry, index->node);
  5623.       else
  5624.     execute_string ("* %s: %s.\n", index->entry, index->node);
  5625.       flush_output ();
  5626.     }
  5627.   printing_index = 0;
  5628.   free (array);
  5629.   close_paragraph ();
  5630.   filling_enabled = previous_filling_enabled_value;
  5631.   inhibit_paragraph_indentation = old_inhibitions;
  5632. }
  5633.  
  5634.  
  5635. /* **************************************************************** */
  5636. /*                                    */
  5637. /*            Making User Defined Commands            */
  5638. /*                                    */
  5639. /* **************************************************************** */
  5640.  
  5641. void
  5642. define_user_command (name, proc, needs_braces_p)
  5643.      char *name;
  5644.      VFUNCTION *proc;
  5645.      int needs_braces_p;
  5646. {
  5647.   int slot = user_command_array_len;
  5648.   user_command_array_len++;
  5649.  
  5650.   if (!user_command_array)
  5651.     user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *));
  5652.  
  5653.   user_command_array = (COMMAND **) xrealloc (user_command_array,
  5654.                           (1 + user_command_array_len) *
  5655.                           sizeof (COMMAND *));
  5656.  
  5657.   user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND));
  5658.   user_command_array[slot]->name = savestring (name);
  5659.   user_command_array[slot]->proc = proc;
  5660.   user_command_array[slot]->argument_in_braces = needs_braces_p;
  5661. }
  5662.  
  5663. /* Make ALIAS run the named FUNCTION.  Copies properties from FUNCTION. */
  5664. void
  5665. define_alias (alias, function)
  5666.      char *alias, *function;
  5667. {
  5668. }
  5669.  
  5670. /* Some support for footnotes. */
  5671.  
  5672. /* Footnotes are a new construct in Info.  We don't know the best method
  5673.    of implementing them for sure, so we present two possiblities.
  5674.  
  5675. MN   1) Make them look like followed references, with the reference
  5676.         destinations in a makeinfo manufactured node or,
  5677.  
  5678. BN   2) Make them appear at the bottom of the node that they originally
  5679.         appeared in.
  5680. */
  5681.  
  5682. #define MN 0
  5683. #define BN 1
  5684.  
  5685. int footnote_style = MN;
  5686. boolean first_footnote_this_node = true;
  5687. int footnote_count = 0;
  5688.  
  5689. /* Set the footnote style based on he style identifier in STRING. */
  5690. void
  5691. set_footnote_style (string)
  5692.      char *string;
  5693. {
  5694.   if (stricmp (string, "MN") == 0)
  5695.     {
  5696.       footnote_style = MN;
  5697.       return;
  5698.     }
  5699.  
  5700.   if (stricmp (string, "BN") == 0)
  5701.     {
  5702.       footnote_style = BN;
  5703.       return;
  5704.     }
  5705. }
  5706.  
  5707. typedef struct fn
  5708. {
  5709.   struct fn *next;
  5710.   char *marker;
  5711.   char *note;
  5712. }  FN;
  5713.  
  5714. FN *pending_notes = (FN *) NULL;
  5715.  
  5716. /* A method for remembering footnotes.  Note that this list gets output
  5717.    at the end of the current node. */
  5718. void
  5719. remember_note (marker, note)
  5720.      char *marker, *note;
  5721. {
  5722.   FN *temp = (FN *) xmalloc (sizeof (FN));
  5723.  
  5724.   temp->marker = savestring (marker);
  5725.   temp->note = savestring (note);
  5726.   temp->next = pending_notes;
  5727.   pending_notes = temp;
  5728.   footnote_count++;
  5729. }
  5730.  
  5731. /* How to get rid of existing footnotes. */
  5732. void
  5733. free_pending_notes ()
  5734. {
  5735.   FN *temp;
  5736.  
  5737.   while ((temp = pending_notes) != (FN *) NULL)
  5738.     {
  5739.       free (temp->marker);
  5740.       free (temp->note);
  5741.       pending_notes = pending_notes->next;
  5742.       free (temp);
  5743.     }
  5744.   first_footnote_this_node = true;
  5745.   footnote_count = 0;
  5746. }
  5747.  
  5748. /* What to do when you see a @footnote construct. */
  5749.  
  5750.  /* Handle a "footnote".
  5751.     footnote *{this is a footnote}
  5752.     where "*" is the marker character for this note. */
  5753. void
  5754. cm_footnote ()
  5755. {
  5756.   char *marker;
  5757.   char *note;
  5758.  
  5759.   get_until ("{", &marker);
  5760.   canon_white (marker);
  5761.  
  5762.   /* Read the argument in braces. */
  5763.   if (curchar () != '{')
  5764.     {
  5765.       line_error ("`@%s' expected more than just `%s'.  It needs something in `{...}'", command, marker);
  5766.       free (marker);
  5767.       return;
  5768.     }
  5769.   else
  5770.     {
  5771.       int braces = 1;
  5772.       size_t temp = ++input_text_offset;
  5773.       size_t len;
  5774.  
  5775.       while (braces)
  5776.     {
  5777.       if (temp == size_of_input_text)
  5778.         {
  5779.           line_error ("No closing brace for footnote `%s'", marker);
  5780.           return;
  5781.         }
  5782.       if (input_text[temp] == '{')
  5783.         braces++;
  5784.       else if (input_text[temp] == '}')
  5785.         braces--;
  5786.       temp++;
  5787.     }
  5788.  
  5789.       len = (temp - input_text_offset) - 1;
  5790.       note = xmalloc (len + 1);
  5791.       strncpy (note, &input_text[input_text_offset], len);
  5792.       note[len] = '\0';
  5793.       input_text_offset = temp;
  5794.     }
  5795.  
  5796.   if (!current_node || !*current_node)
  5797.     {
  5798.       line_error ("Footnote defined without parent node");
  5799.       free (marker);
  5800.       free (note);
  5801.       return;
  5802.     }
  5803.  
  5804.   remember_note (marker, note);
  5805.  
  5806.   switch (footnote_style)
  5807.     {                /* your method should at least insert marker. */
  5808.  
  5809.     case MN:
  5810.       add_word_args ("(%s)", marker);
  5811.       if (first_footnote_this_node)
  5812.     {
  5813.       char *temp_string = xmalloc ((strlen (current_node))
  5814.                        + (sizeof("-Footnotes")) + 1);
  5815.       add_word_args (" (*note %s-Footnotes::)", current_node);
  5816.       strcpy (temp_string, current_node);
  5817.       strcat (temp_string, "-Footnotes");
  5818.       remember_node_reference (temp_string,
  5819.                    line_number, followed_reference);
  5820.       free (temp_string);
  5821.       first_footnote_this_node = false;
  5822.     }
  5823.       break;
  5824.  
  5825.     case BN:
  5826.       add_word_args ("(%s)", marker);
  5827.       break;
  5828.  
  5829.     default:
  5830.       break;
  5831.     }
  5832.   free (marker);
  5833.   free (note);
  5834. }
  5835.  
  5836. /* Non-zero means that we are currently in the process of outputting
  5837.    footnotes. */
  5838. int already_outputting_pending_notes = 0;
  5839.  
  5840. /* Output the footnotes.  We are at the end of the current node. */
  5841. void
  5842. output_pending_notes ()
  5843. {
  5844.   FN *footnote = pending_notes;
  5845.  
  5846.   if (!pending_notes)
  5847.     return;
  5848.  
  5849.   switch (footnote_style)
  5850.     {
  5851.  
  5852.     case MN:
  5853.       {
  5854.     char *old_current_node = current_node;
  5855.     char *old_command = savestring (command);
  5856.  
  5857.     already_outputting_pending_notes++;
  5858.     execute_string ("@node %s-Footnotes,,,%s\n",
  5859.             current_node, current_node);
  5860.     already_outputting_pending_notes--;
  5861.     current_node = old_current_node;
  5862.     free (command);
  5863.     command = old_command;
  5864.       }
  5865.       break;
  5866.  
  5867.     case BN:
  5868.       close_paragraph ();
  5869.       in_fixed_width_font++;
  5870.       execute_string ("---------- Footnotes ----------\n\n");
  5871.       in_fixed_width_font--;
  5872.       break;
  5873.     }
  5874.  
  5875.   /* Handle the footnotes in reverse order. */
  5876.   {
  5877.     FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *));
  5878.  
  5879.     array[footnote_count] = (FN *) NULL;
  5880.  
  5881.     while (--footnote_count > -1)
  5882.       {
  5883.     array[footnote_count] = footnote;
  5884.     footnote = footnote->next;
  5885.       }
  5886.  
  5887.     filling_enabled = true;
  5888.     indented_fill = true;
  5889.  
  5890.     while (footnote = array[++footnote_count])
  5891.       {
  5892.  
  5893.     switch (footnote_style)
  5894.       {
  5895.  
  5896.       case MN:
  5897.       case BN:
  5898.         execute_string ("(%s)  %s", footnote->marker, footnote->note);
  5899.         close_paragraph ();
  5900.         break;
  5901.       }
  5902.       }
  5903.     close_paragraph ();
  5904.     free (array);
  5905.   }
  5906. }
  5907.  
  5908. /*
  5909.  * Local variables:
  5910.  * end:
  5911.  */
  5912.